背景介绍
现在有一款鸭子模拟游戏:SimUDuck。游戏中会出现各种鸭子,一边游泳一边呱呱叫。
设计
很自然的可以想到,先设计一个Duck父类,并且包含两个实现好了的方法swim和quck。并且对于不同鸭子,有着不同的外观,
所以需要一个display方法来描述鸭子的外观。
于是乎,得到了下面这么一个抽象类Duck以及一些它的子类:
业务需求变化
某天客户提出来一个新的需求:我们的鸭子需要会飞,以此来制造差异化,区别于其他竞争者。
很容易想到,只需要在父类Duck上加上fly方法,这样所有的子类都具备了飞行的能力。
但是这样出现了我们不想要出现的情况——一只橡皮鸭也会飞了!
显然橡皮鸭会飞是不符合预期了,一个补救措施就是:类似橡皮鸭的quck方法,覆盖fly方法,并且什么也不错。
如果后续加入诱饵鸭(DecoyDuck),诱饵鸭是木头鸭,既不会飞也不会叫。所以:
现在看来,继承有时并不是一个完美的解决方案。对于父类的修改将会影响到所有子类,从而可能导致子类需要做出对应的修改。
尝试使用接口
如果我们定义Flyable和Quackable,让子类选择实现这两个接口。
从上图就能看出,每有个新子类,就需要考虑是否要实现Flyable和Quackable接口,并且可能它们之中很多实现是相同的。
另一种方案
考虑将鸭子“飞行”和“叫”这两个行为是可变化的部分,将它们取出来单独做封装,然后再组合起来。鸭子类(Duck)中只包含所有鸭子的共同特性。
现在将鸭子的“飞行”和“叫”两个动作“委托”给别人处理,而不是自己实现。
flyBehavior和quackBehavior可以根据具体的子类进行赋值。
最后我们将得到以下类图:
像实现了FlyBehaivor接口的一些列类,我们称之为“算法族”。
策略模式的定义
策略模式定义了算法族,分别封装起来,让它们之间可以相互替换,此模式让算法的变化独立于使用算法的客户。