一、策略模式(Strategy Pattern)《HeadFirst设计模式》读书笔记
1.假设有一个需求,要做一个模拟鸭子的应用。首先很自然的,我们会想到使用继承,先创建一个超类Duck,然后让不同类型的鸭子继承这个超类,复用Duck中的一些属性和功能。
2.这时我们来了一个需求,要求给一个可达鸭添加会飞的功能,为了体现OO的思想,我们不在具体的类中添加功能,还是在Duck类中添加了一个fly()方法以提高代码的复用性。但这样会导致所有类型的鸭子都继承了fly()方法,就连橡皮鸭也会,这显然是不对的。
(1)我们可以在橡皮鸭中重写fly()方法将他覆盖掉,但是以后越来越多的鸭子类型出现后,会导致每次都需要将子类中不需要继承父类的方法覆盖掉,不仅麻烦,整体的逻辑关系也不清晰。
(2)我们可以建立一个Flyable接口,这样想要飞的鸭子只要实现这个接口就可以了。但是还是要在特定的子类中去写实现,同样会很麻烦,而且没有实现代码的复用。
这个问题可以总结为:我们使用继承是想提高代码的复用性,但是有的子类又有自己的特殊需求,如何有条理的按需定制这些方法呢?
3.使用组合:
(1)我们可以将Duck超类中的内容分为两部分:变化的和不会变化的。所有鸭子都具有的特征和行为就是基本不会变化的,我们使用继承处理这一部分,如果以后这些行为需要改变,也只需要在Duck超类中改动就可以了;而fly()行为就是会变化的,不同类型的鸭子会有不同的飞行姿势,还可能不会飞,这一部分我们可以使用组合。
(2)创建一个FlyBehavior接口,对于不同的飞行行为都实现这个接口,对应具体的实现类,如FlyWithWings,FlyNoWay。
public interface FlyBehavior { void fly(); } public class FlyWithWings implements FlyBehavior{ @Override public void fly() { System.out.println("i can fly with wings"); } } public class FlyNoWay implements FlyBehavior { @Override public void fly() { System.out.println("I can't fly"); } }
(3)在Duck中以成员变量的方式组合我们想要的功能,声明成接口类型,如FlyBehavior。再写一个方法叫perfomFly(),在方法内调用FlyBehavior接口的fly()方法,这里又体现了多态的思想。
public abstract class Duck { private String color; FlyBehavior flyBehavior; abstract void display(); //所有的鸭子都会游泳 public void swim(){ System.out.println("all ducks can swim"); } //通过组合实现fly的功能 public void performFly(){ flyBehavior.fly(); } }
而该成员变量接口具体的实例在子类创建对象时,通过构造方法传递进来(暂时,后面会用其他设计模式优化)。额外的也可以通过增加set()方法动态的改变鸭子的行为。
public class PsyDuck extends Duck { @Override void display() { System.out.println("I am a PsyDuck"); } //暂时通过构造方法传入FlyBehavior的实现类 public PsyDuck(FlyBehavior flyBehavior) { this.flyBehavior = flyBehavior; } }
测试一下:
public class MainTest { public static void main(String[] args) { PsyDuck psyDuck = new PsyDuck(new FlyWithWings()); psyDuck.performFly(); } }
打印结果为:
在上面的例子中我们就使用了策略模式。
4.策略模式(Strategy Pattern):
定义:策略模式定义了算法族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。
解释:刚刚我们写的FlyBehavior的两个实现类FlyWithWings和FlyNoWay就是一族算法,说白了就是一组行为,当需要不同具体行为的时候,他们之间可以替换,这些行为发生变化时并不会影响到Duck和它的子类,因为在Duck中我们是使用多态的方式通过接口声明的。
5.总结:
(1)将变化的部分封装起来
(2)使用组合
(3)使用多态,面向接口编程