内容简介:当我们有一堆鸭子,各个鸭子都有相同的游泳(swim),自我描述(display)方法,因为它们都会游泳和自我描述;但是不同的鸭子又有自己特有的飞行(fly),叫声(quack),这时如何使用设计模式来解决这样的问题呢?首先会想到的方法是,有一个鸭子超类,swim和diplay作为公有的方法,不同鸭子的子类去覆盖实现各自的fly和quack方法,貌似就可以解决这个问题了不过,每当新的鸭子类型出现,就需要重新去实现fly和quack方法;甚至如果多个鸭子类型拥有相同的fly或quack方法,代码就不能很好地复用
策略模式
问题引入
当我们有一堆鸭子,各个鸭子都有相同的游泳(swim),自我描述(display)方法,因为它们都会游泳和自我描述;但是不同的鸭子又有自己特有的飞行(fly),叫声(quack),这时如何使用 设计模式 来解决这样的问题呢?
首先会想到的方法是,有一个鸭子超类,swim和diplay作为公有的方法,不同鸭子的子类去覆盖实现各自的fly和quack方法,貌似就可以解决这个问题了
不过,每当新的鸭子类型出现,就需要重新去实现fly和quack方法;甚至如果多个鸭子类型拥有相同的fly或quack方法,代码就不能很好地复用了。
在这里,鸭子的行为在子类里不断地改变,并且让所有子类都拥有这些行为是不恰当的。继承并不能很好地解决问题
设计原则
设计模式中的一个设计原则是: 找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起。
换句话说,如果每次新的需求一来,都会使某方面的代码发生变化,那么你就可以确定,这部分的代码需要被抽出来,和其他稳定地代码有所区分。
这个原则的另一个思考方式是,把会变的部分取出来并封装起来,以便以后可以轻易地改动或扩充此部分,而不影响不需要变化的其他部分。
这样的概念很简单,几乎是每个设计模式背后的精神所在。所有的模式都提供了一套方法 让系统中的某部分改变不会影响其他部分 。
重新设计
是时候把鸭子的行为抽取出来了
-
分开变化和不变化的部分
- 变化的部分:quack,fly
- 不变化的部分:swim display
因此,将quack和fly行为抽取出来:
type basicDuck interface { quackBehaviour flyBehaviour display() swim() } type duck struct { quackBehaviour flyBehaviour name string } func (d *duck) display() { fmt.Printf("I am %+v\n", d.name) } func (d *duck) swim() { fmt.Printf("I am %+v, I can swim\n", d.name) }
可以看到,quack和fly作为两个接口被抽离了出来,而display和swim仍然作为鸭子固定不变的部分
看下quack和fly的部分
type quackBehaviour interface { quack() }
type flyBehaviour interface { fly() }
我们利用接口代表每个行为,比方说quackBehaviour和flyBehaviour,而行为的每个实现都将实现其中的一个接口;实际的实现就不会被绑死在鸭子的子类中了,即具体的行为编写在实现了quackBehaviour和flyBehaviour的子类中;
这样的设计,可以让飞行和呱呱叫的动作被其他的对象复用,因为这些行为已经与鸭子无关了;而我们可以新增一些行为,不会影响到既有的行为类,也不会影响到现有的鸭子子类。
现在实现了以下几个飞行行为类:
type flyWithWings struct { } func (f *flyWithWings) fly() { fmt.Println("fly with wings!") } type cantFly struct { } func (f *cantFly) fly() { fmt.Println("cant fly...") } type flyWithRocketPower struct { } func (f *flyWithRocketPower) fly() { fmt.Println("fly with rocket power!") }
以及以下几个呱呱叫行为类:
type quackWithGuaGua struct { } func (q *quackWithGuaGua) quack() { fmt.Println("quack with guagua!") } type quackWithGuGu struct { } func (q *quackWithGuGu) quack() { fmt.Println("quack with gugu!") } type quackWithEngine struct { } func (q *quackWithEngine) quack() { fmt.Println("quack with engine!") }
这时,是时候整合鸭子的行为了。例如我们有一个火箭动力鸭,该如何实现这个鸭子呢?
type rocketDuck struct { *duck } func main() { d := rocketDuck{ &duck{ &quackWithEngine{}, &flyWithRocketPower{}, "rocket duck", }, } doAction(d) } func doAction(d basicDuck) { d.display() d.swim() d.quack() d.fly() }
运行之,看效果:
I am rocket duck I am rocket duck, I can swim quack with engine! fly with rocket power!
总结
这就是策略模式,即:定义了算法族,分别封装起来,让它们之前可以相互替换,此模式让算法的变化独立于使用算法的客户
参考文章
《head first设计模式》
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- 设计模式——订阅模式(观察者模式)
- 设计模式-简单工厂、工厂方法模式、抽象工厂模式
- java23种设计模式-门面模式(外观模式)
- 设计模式-享元设计模式
- Java 设计模式之工厂方法模式与抽象工厂模式
- JAVA设计模式之模板方法模式和建造者模式
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。