背景
有一款鸭子游戏,系统中有很多种类的鸭子,所有鸭子都会游泳和呱呱叫。类图如下:
现在需要给鸭子添加飞行动作,让鸭子可以飞起来。
第1版实现
在父类Duck中新增fly方法,实现鸭子的飞行动作。
游戏运行之后发现,有一只橡皮鸭也飞了起来,橡皮鸭怎么会飞呢?
问题:并不是所有的鸭子都会飞,如果让不会飞的鸭子拥有飞行动作就会很奇怪
这个好解决:在所有不会飞的鸭子类中重写fly方法,什么也不做
如果系统中还有N(可以想象N很大)种鸭子不会飞,就得重写N个类,工作量好像有点大
那有没有一种方法,既可以让部分鸭子飞,又不用重写N个类呢?
第2版本实现
从第1版实现中的问题可知:对鸭子而言,飞行动作不是一个固有行为,并不是所有的鸭子都会飞,所以由父类实现是不合理的。对于这种变化的行为,可以考虑从系统中剥离出来。
先声明一个接口:FlyAction
再实现两个具体的飞行类:FlyWithWings和FlyNoWay
然后在父类Duck中新增1个FlyAction属性和2个方法(Objective-C)
@property (nonatomic, strong) id<FlyAction> flyAction;
- (void)setFlyAction:(id<FlyAction>)flyAction {
_flyAction = flyAction;
}
- (void)performFly {
if ([self.flyAction respondsToSelector:@selector(fly)]) {
[self.flyAction fly];
}
}
最后,在使用鸭子类的地方,给鸭子动态设置不同的飞行动作
- (void)test {
// 会飞的鸭子
Duck *duck1 = [RedHeadDuck new];
[duck1 setFlyAction:[FlyWithWings new]];
[duck1 performFly];
// 不会飞的鸭子
Duck *duck2 = [RubberDuck new];
[duck2 setFlyAction:[FlyNoWay new]];
[duck2 performFly];
}
后续如果新增一种新的飞行动作,也不需要修改鸭子类中的代码。
其实,这种设计就是采用了策略模式。
策略模式
策略模式的结构:
(1)Strategy:抽象策略类,定义公共接口,Context类通过该接口调用具体策略
(2)ConcreteStrategy:具体策略类,实现具体的策略算法
(3)Context:上下文,策略的使用者,持有Strategy对象的引用
对于鸭子系统而言,FlyAction就是Strategy,鸭子可以用翅膀飞,可以借助飞行器,也可以不会飞,这些都是一种策略,而Duck就是Context,可以使用不同的飞行策略。
策略模式的定义:定义了一系列算法,并把它们都封装起来,使它们之间可相互替换。
总结
什么情况下适合使用策略模式呢?
第1种情况:如果系统中很多相关的类仅仅是行为有差异,则可以将这些行为抽象为策略
第2种情况:一个类的多种行为通过条件语句实现,则可以将这些条件分支抽象为策略