ps:本文主要来源于Head First 设计模式(抄Head First的),如有不懂请购买原书观看。
策略模式定义: 定义了算法簇,分别封装起来,让他们之间可以相互替换,此模式让算法的变化独立于使用算法的客户。
举个例子:
假设有个Duck(鸭子)父类,鸭子有很多子类,都继承于duck,比如MallardDuck,RedheadDuck, RubberDuck,DecoyDuck.
Duck 类有 quack(),swim(),display(), fly()等方法。这样子类都有了父类的方法。这样有个缺点,比如有的子类不该有fly()这个方法,现在也有了,
RubberDuck (橡皮鸭)就不能飞。
解决这个问题有一个方法,就是把fly()方法覆盖掉,不做任何事就ok了。这样本也没什么大问题,但当我加入了新的鸭类后,比如加了 DecoyDuck(诱饵鸭),也不会飞,这样又要覆盖掉fly()方法。这样代码就重复了。
那么把 fly()方法提前出来,放进一个Flyable接口中,接口有fly()方法,那个鸭子类需要fly()行为,就实现Flyable接口。这样是能解决了RubberDuck与DecoyDuck鸭的飞行问题,这两类鸭再也不会飞。但这又带来新问题,就是不能复用fly()方法了,本来很多类鸭子的fly()都差不多(甚至一样),现在要一个一个实现。
ps: 1.把应用中可能需要变化的部分独立出来,不要和那些不需要变化的代码混到一起。
2.针对接口编程,而不是针对实现编程,即(Animal animal = new Dog()或者Map map = new HashMap(),而不是 Dog d=new Dog,)
回到上面的问题,解决的办法仍然是把fly()抽象出来成为一个接口,接口可以叫FlyBehavior,或者别的FlyInteface也行,接着针对不同的飞行行为写不同的实现类,比如 FlyWithWings,FlyNotWay类实现FlyBehavior,在类里面实现不同飞行方式。这样设计,可以让飞行动作被其它对象复用,不仅仅是鸭子类,这个飞行行为已经与鸭子类无关了。还可以在FlyWithWings里面增加别的动作比如run()等,也不会影响鸭子类使用fly()。
这样Duck类可以使用FlyBehavior了,在Duck类 新加 FlyBehavior flyBehavior;
可以在Duck构造器初始化FlyBehavior,
public class MallardDuck extends Duck{
public MallardDuck(){
flyBehavior = new FlyWithWings();
}
public void performFly(){
flyBehavior.fly()
}
}
这样鸭子不必亲自出来fly()动作了,可以委托(delegate)给别人处理(FlyBehavior).
上面已经很好了,但仍有不足,就是我们在MallardDuck构造器初始化 FlyBehavior 为FlyWithWings实例,这是针对具体实现编程,如何动态设定这个行为呢,就是我本来飞行行为是FlyWithWings,后来我又想换成FlyWithRockey。
可以在Duck类新加set方法,
public void setFlyBehavior(FlyBehavior fb){
flyBehavior = fb;
}
来看一下部分代码
Duck model = new ModelDuck();
model.performFly(); //这里输出 构造器里初始化的飞行方法 比如 i am fly with wings
model.setFlyBehavior(new FlyRockerPowered());
model.performFly(); //这里输出修改后的飞行方法,比如 i am fly with rocker powered
ps:多用组合,少用继承。(“有一个”可能比“是一个”更好)
我们描述事物的方式有些改变,不再把鸭子的行为说成“一组行为”,我们把行为想成是“一簇算法”。(这样的做法也能很容易用一群类来计算不同州的销售税金)。
策略模式在jdk中的应用有比较器Comparator和布局管理器LayoutManager