策略模式
- 完成一项任务,往往可以有多种不同的方式,每一种方式称为一个策略,我们可以根据环境或者条件的不同选择不同的策略来完成该项任务。
定义个Duck类,Duck的display都是统一的,不然也不会被称之为Duck。但是quack、swim、fly就是可能变化之处。fly可能是用翅膀fly,也有可能是通过火箭推进器fly。
定义方法1:定义一个父类Duck,有display、quack、swim、fly四个方法(或抽象方法)。其他鸭子通过继承Duck类获取父类方法,有变化时,重写父类的方法。
弊端:当子类不需要某个父类方法时,无法不继承父类方法。
必须橡皮鸭子,根本不能飞。只要继承了Duck,就不得不飞了。
定义方法2:定义一个父类Duck,只有一个方法display。其他鸭子通过继承Duck类获取display方法,然后各自实现自己的quack、swim、fly。
弊端:不能复用方法。
不同颜色的鸭子,fly的方式都是用翅膀。但是每一个子类继承Duck类的时候都需要重新实现一遍相同的fly方法。
定义方法3:定义一个Flyable接口,会飞的鸭子都实现Flyable接口。
弊端:不能复用方法。
效果等同于方法2。
定义方法4:
定义一个Duck基类。
public abstract class Duck {
public void display() {
System.out.println("A Duck.");
}
}
定义一个接口FlyOperator。
public interface FlyOperator {
void fly();
}
实现“用翅膀飞”的行为类。
public class FlyWithSwing implements FlyOperator {
@Override
public void fly() {
System.out.println("flying...");
}
}
定义一个可以用翅膀飞的鸭子。
public class RedDuck extends Duck {
public FlyOperator flyOperator;
public void fly() {
flyOperator.fly();
}
public FlyOperator getFlyOperator() {
return flyOperator;
}
public void setFlyOperator(FlyOperator flyOperator) {
this.flyOperator = flyOperator;
}
}
定义:策略模式定义了算法族,分别封装起来,让他们可以互相替换,这种模式让算法的变化独立于使用算法的客户。
设计原则:
一、把应用中可能变化的之处,与不需要变化之处独立。
减少不会变化之处的代码量,提升可能变化之处的代码弹性。
如上述案例,不管是通过继承、接口实现还是组合(将两个类结合起来使用),都是将可变内容独立。
定义方法4的优势一,将“鸭子”与“行为”独立,“行为”可以被拓展、改变,而不会影响到“鸭子”。
优势二,“行为”可以被复用。
二、针对接口编程,而不是针对实现编程。
定义方法1、2的行为都是来自于父类的实现;定义方法3的行为来自于对接口的实现,这三种方法都依赖于“实现”,被实现绑死后,就没办法更改行为。除非写更多代码。
定义方法4使用接口表示行为,优势三:在运行时动态地改变行为。实际的实现不会与Duck的子类绑死(换句话说,就是具体的实现被定义在在接口的实现类)。
三、多用组合,少用继承。
将两个类结合起来用,如定义方法4,就交组合。
定义方法4中使用定义RedDuck 时,将接口FlyOperator定义为一个属性,目的是可以动态改变FlyOperator的实现。“组合”与继承的区别在于,鸭子的行为不是继承来的,而是和适当的行为对象“组合”而来。
组合可以同时实现如上优势。
设计原则的目的:提高可维护性、可拓展性。