Nicky's Blog

设计模式学习笔记(一)-策略模式

最终开始写学习笔记,重新翻起几年前买的的书《headfrist-设计模式》,今天主要一起学习下策略模式。

需求

“如果公司给了这个需求:
模拟鸭子游戏,里面是各种各样的鸭子,有各种各样的玩法….
….”

思考实现

一般我们的做法是写一个父类 Duck,思考鸭子共同的属性,然后让业务方继承Duck鸭子的基本属性来扩展。
咦?看起来做法不错哦。

–突然,业务方提到一个新需求,我们需要让鸭子具备飞行的能力。
–额,没问题啊,我们在父类,扩展这种能力即可,子类来选择复用或者覆盖
–但是,加到父类里面,针对不会飞的鸭子类型,好像不太合理,并不是每种鸭子都具备飞的能力,咋办?

–或者我们可以尝试,把飞行和叫的功能,独立成一个个接口功能即可,每个业务方去实现这些接口,然后自己去实现具体的逻辑即可?

–but,这样,每个鸭子都需要自己去实现飞行的方法,或许这里飞行的方法是一致呢,导致不能复用,而且,不断新增功能,我们都需要在每个鸭子上面新增接口,会增加一堆接口。
–那还有什么方法呢?

设计原则:找出应用中可能变化之处,把他们独立出来,不要和那些不需要变化的代码混在一起。

这个时候,我们需要把会变化的部分取出并封装起来,以便以后可能轻易改动或者扩充此部分,而不影响不需要变化的其他部分。

也就是我们需要把鸭子的一些行为提取出来,比如,我们知道鸭子类里面的fly和quack方法不同的鸭子会不同,我们把这些行为独立出来。那具体怎样实现呢?

我们利用接口代表每个行为,比如FlyBehavior和QuackBehavior,而行为的每个实现来依次实现其中的接口。

1
2
3
4
5
class FlyWithWings implents FlyBehavior//会飞的
class FlyNoWay implents FlyBehavior//不会飞的
class Quack implents QuackBehavior//会叫的
class MuteQuack implents QuackBehavior //静音的

这样,所有的特性被独立出来了,不同的鸭子选用不同的行为即可

设计原则:针对接口编程,而不是针对实现编程

1
2
3
4
5
6
7
8
9
10
11
12
13
public class Duck {
FlyBehavior mFlyBehavior;
QuackBehavior mQuackBehavior;
....
public void performFly(){
mFlyBehavior.fly();
}
public void performQuack(){
mQuackBehavior.quack();
}
}

那具体业务方怎么用呢?

1
2
3
4
5
6
7
8
9
10
public class MuteDuck extents Duck{
//构造的时候初始化行为实例
public MuteDuck{
mQuackBehavior = new MuteQuackBehavior();
mFlyBehavior = new FlyNoWayBehavior();
}
...
}

目前来看,貌似在构造的时候,就实例化这些,貌似也不是那么合理,感觉强绑定了,那还有其他方法吗?如何动态的设置行为属性呢?

1
2
3
//添加这几个方法
public void setFlyBehavior(FlyBehavior fb);
setxxx();

//这样,在实际调用就可以随便设置不同的行为模式进去了,而不是构造就绑定所有。

每个鸭子都拥有FlyBehavior和QuackBehavior,鸭子的行为不是继承来的,而是和适当的行为对象组合来的,每个Behavior里面可以独立的类去实现扩展具体行为,而不在父类里面做具体实现,做到了自由组合和扩展,而不相互影响。

设计原则:多用组合,少用继承。

总结

策略模式 定义了算法族,分别封装起来,让他们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。

  1. 找出应用中可能变化之处,把他们独立出来,不要和那些不需要变化的代码混在一起。
  2. 针对接口编程,而不是针对实现编程
  3. 多用组合,少用继承。
  4. 同行里面用设计模式沟通,将事半功倍,设计圈的共同语言