策略模式(Strategy Pattern):分别封装接口,实现算法族,超类里面放行为接口对象,在子类里面具体设定行为接口对象,原则就是分离变化部分,封装接口,基于接口编程各种功能。此模式让行为算法的变化独立于算法的使用者。
使用策略模式(同样针对于所有的方案设计)的时候,需要应对项目的扩展性,降低复杂度。分析项目中变化与不变的部分,提取变化的部分,抽象成接口+实现。
下面使用一个简单的例子来演示一下策略模式的使用(鸭子的例子)
分析:鸭子有很多特点,这里用叫声这个特点来作为演示,比如有红头鸭、绿头鸭,叫声有“嘎嘎”叫,也有“咯咯”叫等,这种行为就是变化的,行为的变化还体现在以后可能添加一个新属性飞行,因为可能有可飞行、不可飞行等多种,因此,飞行也是变化的行为。
为了更好的展示策略模式的优势,我们先用传统的方式来实现上面的案例设计
1、定义抽象类
public abstract class Duck { abstract void quack(); //叫声 }
2、子类
public class GreenHeadDuck extends Duck { @Override void quack() { System.out.println("嘎嘎"); } }
public class RedHeadDuck extends Duck { @Override void quack() { System.out.println("咯咯"); } }
现在就是有个问题,比如新增了 一个飞行行为,由于不同的鸭子飞行的特点不同,因此也需要定义在超类里面,由子类重写具体内容,然后这里就有个问题:不同的鸭子品种可能存在同样的飞行方式,这种方式就会导致重复代码,增加工作量。
别急,现在就该策略模式闪亮登场了(鼓掌)。
首先将不同的行为封装成接口,以及实现该行为接口
1、叫声行为
public interface IQuack { void quack(); }
public class GaGaQuack implements IQuack { @Override public void quack() { System.out.println("嘎嘎"); } }
public class GeGeQuack implements IQuack { @Override public void quack() { System.out.println("咯咯"); } }
2、飞行行为
public interface IFly { void fly(); }
public class CanFly implements IFly { @Override public void fly() { System.out.println("可以飞行"); } }
public class NoFly implements IFly{ @Override public void fly() { System.out.println("不会飞行"); } }
3、鸭子抽象类
public abstract class Duck { IQuack iQuack; IFly iFly; public void quack(){ iQuack.quack(); } public void fly() { iFly.fly(); } //提供动态设置的方法 public void setiQuack(IQuack iQuack) { this.iQuack = iQuack; } //提供动态设置的方法 public void setiFly(IFly iFly) { this.iFly = iFly; } }
public class GreenHeadDuck extends Duck { public GreenHeadDuck() { iFly = new CanFly(); iQuack = new GaGaQuack(); } }
public class RedHeadDuck extends Duck { public RedHeadDuck() { iFly = new NoFly(); iQuack = new GeGeQuack(); } }
在子类中给超类中的行为抽象类的引用实例化,这种方式使得新增行为更加方便,提高复用性。
最后重申一下策略模式的注意点:
1、分析项目中的变化部分(当前存在的变化部分以及项目后期迭代可能存在的变化部分)与不变化部分
2、多用组合,少用继承,用行为类的组合,而不是行为的继承(继承会使得父类的影响扩展到所有子类),更有弹性