Go實現設計模式--策略模式

Go語言中的方法和接口

go語言中有方法接口

方法

在函數的func和函數名間增加一個特殊的接收器類型,接收器可以是結構體類型或非結構體類型。接收器可以在方法內部訪問。創建一個接收器類型爲Type的methodName方法。

func (t Type) methodName(parameter list) {
}

接口

接口是方法(方法簽名,method signature)的集合。當一個類型定義了接口中的所有方法,就稱它實現了該接口。與OOP類似,接口定義了一個類型應該具有的方法,由該類型決定如何實現這些方法。

type myInterface interface{
    method1()
    method2()
}

例子

存在名爲Animal的interface,其內有方法makeSound()
存在Dog,Cat的結構體,實現了makeSound()方法,即實現了接口Animal中的所有方法
則Dog,Cat就可以視作是Animal來使用,實現多態,也就是接口的不同實現方式

package main

import "fmt"

type Animal interface {
	makeSound()
}

type Dog struct {
}

func (t Dog) makeSound() {
	t.bark()
}

func (t Dog) bark(){
	fmt.Println("wang wang")
}

type Cat struct {
}

func (t Cat) makeSound() {
	t.meom()
}

func (t Cat) meom(){
	fmt.Println("miao miao")
}


func sound(a Animal){
	a.makeSound()
}

func main() {
	dog := new(Dog)
	cat := new(Cat)
	sound(dog)
	sound(cat)
}

輸出:

wang wang
miao miao

策略模式

定義

定義算法族,分別封裝起來,讓它們之間可以互相替換,此模式讓算法變化獨立於使用算法的用戶。

介紹

意圖:定義一系列的算法,把它們一個個封裝起來, 並且使它們可相互替換。

主要解決:在有多種算法相似的情況下,使用 if…else 所帶來的複雜和難以維護。

何時使用:一個系統有許多許多類,而區分它們的只是他們直接的行爲。

如何解決:將這些算法封裝成一個一個的類,任意地替換。

關鍵代碼:實現同一個接口。

應用實例: 1、諸葛亮的錦囊妙計,每一個錦囊就是一個策略。 2、旅行的出遊方式,選擇騎自行車、坐汽車,每一種旅行方式都是一個策略。 3、JAVA AWT 中的 LayoutManager。

優點: 1、算法可以自由切換。 2、避免使用多重條件判斷。 3、擴展性良好。

缺點: 1、策略類會增多。 2、所有策略類都需要對外暴露。

使用場景
1、如果在一個系統裏面有許多類,它們之間的區別僅在於它們的行爲,那麼使用策略模式可以動態地讓一個對象在許多行爲中選擇一種行爲。
2、一個系統需要動態地在幾種算法中選擇一種。
3、如果一個對象有很多的行爲,如果不用恰當的模式,這些行爲就只好使用多重的條件選擇語句來實現。

注意事項:如果一個系統的策略多於四個,就需要考慮使用混合模式,解決策略類膨脹的問題。

代碼示例

1.定義duck類,是所有鴨子的集合,其內含有FlyBehavior,QuackBehavior 分別是所有飛行行爲的集合及鴨子叫行爲的集合

type FlyBehavior interface {
	fly()
}

type QuackBehavior interface {
	quack()
}

//策略類:
//設計一個鴨子類,同時有飛行行爲,叫行爲
//擁有兩個接口:performQuack,performFly
type Duck struct {
	flyBehavior FlyBehavior
	quackBehavior QuackBehavior
}

2.有了以上結構體,定義鴨子叫,就是使用實現了QuackBehavior對象的quack()方法具體實現參考performQuack,飛行行爲類似,將行爲與鴨子剝離開

func (d Duck) performQuack() {
	d.quackBehavior.quack()
}

func (d Duck) performFly() {
	d.flyBehavior.fly()
}

3.因爲duck類內有兩個Behavior,所以要對其進行初始化,分別以具體的實現了兩個Behavior的對象初始化對duck賦值才能使用其內的兩個behavior,具體實現參考NewDuck

func NewDuck(fb FlyBehavior,qb QuackBehavior) *Duck{
	return &Duck{
		flyBehavior:fb,
		quackBehavior:qb,
	}
}

4.因爲goloang沒有繼承,所以要使用duck可以通過struct間使用匿名引入的方式實現對象屬性方法的組合

type MallardDuck struct{
	*Duck
}

5.可以動態的改變子結構體內的方法

func (d MallardDuck) setQuack(behavior QuackBehavior) {
	d.quackBehavior = behavior
}

整體可運行代碼如下

package main

import "fmt"

type FlyBehavior interface {
	fly()
}

type QuackBehavior interface {
	quack()
}

//策略類:
//設計一個鴨子類,同時有飛行行爲,叫行爲
//擁有兩個接口:performQuack,performFly
type Duck struct {
	flyBehavior FlyBehavior
	quackBehavior QuackBehavior
}
//策略類操作方法
func (d Duck) performQuack() {
	d.quackBehavior.quack()
}

func (d Duck) performFly() {
	d.flyBehavior.fly()
}
/*策略類構造函數*/
func NewDuck(fb FlyBehavior,qb QuackBehavior) *Duck{
	return &Duck{
		flyBehavior:fb,
		quackBehavior:qb,
	}
}

type FlyWithWings struct{
}

func (d FlyWithWings) fly() {
	fmt.Println("I can fly by wings.")
}

type QuackOne struct{
}

func (d QuackOne) quack() {
	fmt.Println("I can Quack.")
}

type QuackTwo struct{
}

func (d QuackTwo) quack() {
	fmt.Println("I can not quack.")
}

//因爲結構體沒有繼承所以可以用匿名字段來實現繼承
//但是不能以此結構體作爲父類使用
type MallardDuck struct{
	*Duck
}

func (d MallardDuck) display() {
	fmt.Println("MallardDuck")
}

func (d MallardDuck) setQuack(behavior QuackBehavior) {
	d.quackBehavior = behavior
}

func perform(d Duck){
	d.quackBehavior.quack()
	d.flyBehavior.fly()
}

func main() {
    f1 :=  new(FlyWithWings)
    q1:=new(QuackOne)
    q2:=new(QuackTwo)

    var md1 MallardDuck

    md1.Duck = NewDuck(f1,q1)
	perform(*md1.Duck)
    md1.display()
    md1.setQuack(q2)
    md1.performQuack()
}

運行結果

I can Quack.
I can fly by wings.
MallardDuck
I can not quack.

參考文章:
https://www.runoob.com/design-pattern/strategy-pattern.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章