在策略模式(Strategy Pattern)中,一个类的行为或其算法可以在运行时更改。这种类型的设计模式属于行为型模式。在策略模式中,我们创建表示各种策略的对象和一个行为随着策略对象改变而改变的 context 对象。策略对象改变 context 对象的执行算法。
背景介绍:
有一个设计好的基类Duck类,有quack,swim和display方法等等。然后还有两个子类,继承Duck。如下图:
现在有一个新的需求,给鸭子添加一个新的方法,飞行。
考虑1:
直接在基类里面添加一个fly方法。
结果发现,这样子写显然不合理,不是所有的鸭子类都有飞行的方法,比如,橡皮鸭,诱饵鸭(一种木头假鸭,不会飞也不会叫)就没有。
考虑2:
可以把橡皮鸭中的fly方法注释掉,就好像覆盖掉quack方法一样。
但是,这个方案显然也不好,因为,每当要有新的鸭子子类出现的时候,就要去被迫检查并可能需要覆盖的fly方法和quack方法,这简直是一个噩梦。,所以需要一个方法,让某些鸭子可以飞或者可以叫。
考虑3
把fly方法取出来,放到一个flyable接口中,这样以来,只有会飞的鸭子才实现这个接口,同样,也可以设计一个quackable接口,因为也并不是所有的鸭子都会叫。
其实,这个方法很笨,这样子一来,会有很多重复的代码,每一个有fly方法的子类,都需要在子类中把这些方法实现一下。而且如果方法实现发生变化,又需要在这些有fly方法的子类中一个一个的修改代码,费时费力。还容易出错。
思考:那到底,我们应该怎么设计这个Duck类呢?
想到OOP的一个设计原则,开闭原则,对修改关闭,对扩展开放。
换句话说,每次新的需求一来,都会使某方面代码发生变化,那么,这部分代码就要抽离出来,和其他稳定的代码有所区别。而Duck就是那个稳定的部分。
考虑4
可以这么设计,对于飞行的行为,设计一个接口,FlyBehavior,所有的飞行类都实现这个接口。这么一来,就有了继承的“复用”好处,却没有继承带来的包袱。设计如下图:
整合:
1,首先,在Duck类中“加入两个实例变量”,并且声明为接口类型。并将Duck类中的fly方法删除。
2,现在,实现perfoemQuack方法
3,写一个继承Duck的子类
Duck里面有两个实例变量,且是接口类型的,其中一个是quackBehavior,然后他还有一个方法,叫performQuack,里面的实现部分是,鸭子对象并不亲自处理呱呱叫这个行为,而是委托给quackBehavior引用的对象,换言之,quackBehavior对象指向哪个实现它的类,就调用的实现类中的对象的方法。
这样子做的好处就是,每当添加一个新功能,只需要对原有的代码进行扩张,对于已经写好的代码,并不需要进行修改,从而带来意想不到的错误。