定义
策略模式定义了一系列算法,将每个算法封装起来,并且使他们之间可以相互替换,策略算法让算法的变化不会影响到使用算法的客户。策略模式类图见下图:
(1)Strateegy:定义了一个共同的接口。所有的具体算法类实现这个接口。环境(上下文)使用这个接口调用具体的算法类。
(2)ContrConcreteStrategy:封装了具体的算法,实现了一个接口。
(3)Context:环境(上下文类)。用于配置一个具体的算法策略对象,维持一个策略接口类型的参考,并且可以定义一个让接口Strategy的具体对象方位的接口。简单情况下,context类可以省略。
案例
鸭子应用(headfirst设计模式copy过来)
JOE做了一套成功的模拟鸭子游戏:SimUDuck,游戏中出现各种鸭子,一边游戏戏水,一边呱呱叫,设计了一个鸭子超类,让各种鸭子继承此超类。
现在要加一个鸭子飞的功能:在超类duck中加入fly的方法,让其他子类继承来实现功能。
但是,这样的话会出现一个问题,并不是所有的鸭子都会飞,如橡皮鸭。若在子类中重写飞的方法,变成什么都不做,再来一个诱饵鸭,不会飞也不会叫,再在诱饵鸭中覆盖掉叫和飞的方法。
利用继承提供duck的行为,导致缺点有以下几点:
- 代码在多个子类中重复
- 运行时的行为不易改变
- 难以得知所有鸭子的全部行为
- 改变牵一发动全身,造成其他鸭子不想要的改变
改进,建立飞行和叫的接口,让会飞的实现飞行的接口,会叫的实现叫的接口
上面的继承方式和接口方式的缺点:
- 继承方式,改变鸭子的行为会影响所哟种类的鸭子,不恰当
- 接口方式:Java接口不具有实现代码,继承接口无法达到代码的复用
设计原则:找出应用中可能需要变化之处,把他们独立出来,不要和那些需要变化的代码混在一起
分开变化和不变化的东西,建立两组类一个是fly相关的,一个是quack相关的,每一组类将实现各自的动作
设计原则:针对接口编程,而不是针对实现编程
我们希望一切有弹性,毕竟正是因为一开始鸭子的行为没有弹性,才开始这样的设计,指定行为到鸭子实例。应该在鸭子类中包含设定行为的方法。就可以在运行时动态改变鸭子的行为。利用接口代表行为,行为的每个实现都必须实现这些接口之一
设计原则:多用组合,少用继承
在模拟鸭子的例子中,鸭子的行为不是继承而来,而是和适当的行为对象组合而来,“有一个”可能比“是一个”更好
代码实现
public abstract class Duck {
FlyBehavior flyBehavior;
QuackBehavior quackBehavior;
public Duck(){
}
public abstract void display();
//委托给行为类
public void performFly(){
flyBehavior.performFly();
}
public void performQuack(){
quackBehavior.performQuack();
}
public void swim() {
System.out.println("All ducks float, even decoys!");
}
}
public class MallardDuck extends Duck{
public MallardDuck() {
quackBehavior = new Quack();
flyBehavior = new FlyWithWings();
}
@Override
public void display() {
System.out.println("I'm a real Mallard duck");
}
}
//飞行行为的接口
public interface FlyBehavior {
public void performFly();
}
public class FlyWithWings implements FlyBehavior{
@Override
public void performFly() {
System.out.println("I'm flying!!");
}
}
//叫的行为接口
public interface QuackBehavior {
public void performQuack();
}
public class Quack implements QuackBehavior {
@Override
public void performQuack() {
System.out.println("Quack");
}
}
//测试类
public class MiniDuckSimulator {
public static void main(String[] args) {
Duck mallard = new MallardDuck();
mallard.performFly();
mallard.performQuack();
}
}
动态给鸭子设定行为,而不是在鸭子的构造器内实例化。在duck类中加两个方法,实现动态指定其行为
public void setFlyBehavior(FlyBehavior flyBehavior){
this.flyBehavior = flyBehavior;
}
public void setQuackBehavior(QuackBehavior quackBehavior){
this.quackBehavior = quackBehavior;
}