策略模式定义
策略模式定义了一个算法族,分别封装起来,使得它们之间可以相互变换。策略让算法的变化独立于使用它的客户。
策略模式中的OO设计原则
1.开发人员在面对代码的设计时,要将应用中变化的部分与不变的部分进行分离。
对于不变的部分,我们通常可以通过基类的继承来完成。
对于变化的部分,如果通过基类的继承来完成,那么可能会造成代码的冗余(因为某些子类根本不需要这个功能),也会造成代码不够灵活,在运行时无法对一个对象所具有的能力进行修改。
2.开发人员在面对代码设计时,要针对接口编程,而不是针对实现编程。
如果针对实现编程,就无法应用面向对象编程的多态性,从而造成代码难以维护。
3.开发人员在面对代码设计时,应优先使用组合而不是继承。
使用组合创建系统可以带来更大的弹性,不仅可以让你把一个算法家族封装进它们自己的一组类,而且让你在运行时改变行为,只要组合的对象实现正确的行为接口。
策略模式示例
要求:要设计一个鸭子应用,这些鸭子可能会具有一些行为(对应编程中的方法),如飞行,叫等。
1.如果设计一个基类,这个类抽象出了所有鸭子应该具有的状态与行为,如果有一个新鸭子,那么这个新鸭子就继承这个基类。
问题:如果这样去设计,那么当出现一个新的鸭子,比如从前的鸭子都是动物,他们自然会飞翔,所以这个抽象基类应该定义fly()方法,但是如果这个新鸭子是一只玩具鸭子,那么它不具备飞翔方法,此时对于新的类这就是一个冗余。
2.按照1中的问题,如果考虑将鸭子的行为抽象成为多个接口,比如飞行类抽象成为一个Flyable类接口。
问题:这样修改了需要在基类中定义,强制子类拥有,如果子类想拥有,那么就实现接口,实现了接口就要实现对应的方法,但是也会出现问题,如果要对某一个具体实现了Flyable类接口的类需要做出飞行状态的修改,那么就需要对其进行代码大改。
3.按照策略模式进行设计。
①针对于1与2中存在的问题,我们可以发现鸭子的一些内容是可以不变的,比如鸭子是什么鸭子,这种方法应该是所有鸭子类共有的(不变的部分),把这些内容可以放入抽象基类,交由所有的鸭子类进行继承。
②对于变化的部分,如鸭子飞行,鸭子叫,这并不是所有鸭子类都能共有的,所以设计称接口,此时会发现,设计成接口也和问题2中出现的问题一样,那么解决方案是,可以设置飞行类接口,但是此接口不由具体的鸭子类实现,而是针对于不同的飞行可以具有的行为,让这些行为去实现飞行类的接口(变化的部分),让抽象鸭子类拿到接口的引用,那么根据多态的原则,我们可以在运行时去设置这些鸭子的飞行。
整体的类设计图如下:
代码实现如下:
public abstract class Duck {
private FlyBehavior flyBehavior;
public abstract String getName();
public void performFly(){
flyBehavior.fly();
}
public FlyBehavior getFlyBehavior() {
return flyBehavior;
}
public void setFlyBehavior(FlyBehavior flyBehavior) {
this.flyBehavior = flyBehavior;
}
}
public interface FlyBehavior {
void fly();
}
public class FlyWithWings implements FlyBehavior {
@Override
public void fly() {
System.out.println("用翅膀飞");
}
}
public class FlyNoWay implements FlyBehavior{
@Override
public void fly() {
System.out.println("这只鸭子不会飞");
}
}
//测试类
public class BlackDuck extends Duck{
private String name;
public BlackDuck(String name) {
this.name = name;
}
public BlackDuck() {
}
public static void main(String[] args) {
Duck duck = new BlackDuck("黑鸭子");
duck.setFlyBehavior(new FlyWithWings());
duck.performFly();
duck.setFlyBehavior(new FlyNoWay());
duck.performFly();
}
@Override
public String getName() {
return name;
}
}