策略模式(strategy pattern) 
 
1.从鸭子仿真程序开始 
假设有一个鸭子仿真程序,Duck类是一个抽象类,定义了鸭子应该具有的行为,鸭子应该会呱呱叫,有quack()函数;所有的鸭子都会游泳,有swim()函数。这两个函数都是实函数。由于每只鸭子都有不同的表现形式,因此display函数是一个虚函数,每个鸭子的子类都需要自己实现这个函数。
2. 添加飞翔功能 这时我们需要为鸭子添加一个飞翔的功能,因此我们需要为Duck类添加一个fly()的函数。于是类的设计就变成了下面这样。
下面是Duck的实现代码:完整的代码 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public  abstract  class  Duck1   {    public  void  quack ()   {         System.out.println("quack" );     }     public  void  swim ()   {         System.out.println("I can swim" );     }     public  abstract  void  display ()  ;          public  void  fly ()   {         System.out.println("I can fly" );     } } 
 
这时问题出现了,并不是每一只鸭子都会飞,直接在Duck类中添加fly函数,意味着所有子类都具有了fly的能力。同样也不是每一只鸭子都会呱呱叫(例如一只玩具鸭子),因此Duck的子类必须重写这些函数以满足自己的场景。
例如下面这个例子,橡皮鸭(玩具)就需要重写quack和fly函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 class  RubberDuck  extends  Duck1   {    @Override      public  void  quack ()   {              }     @Override      public  void  fly ()   {              }     @Override      public  void  display ()   {         System.out.println("RubberDuck" );     }     public  static  void  main (String[] args)   {         RubberDuck r = new  RubberDuck();         r.display();         r.quack();         r.swim();         r.fly();     } } 
 
这样设计的问题是每出现一种新的Duck的子类都需要考虑是否重写quack和fly函数,会出现大量的重复代码
3. 一种解决方案 
一种解决方案是将fly和quack从Duck超类中提取出来做成两个接口Flyable和Quackable。只有拥有这些功能的子类才会去实现这些接口。
这种解决方式破坏了代码重用的原则,每一个子类都必须实现接口中的方法,后期如果需要修改,就需要打开每一个实现了接口方法的类进行修改,后期代码维护将是一个噩梦。
4. 策略模式 定义  策略模式(strategy pattern)defines a family of algorithms,encapsulates(封装) each one, and makes them interchangeable.Strategy lets the algorithms vary independently from clients that use it.
4.1. 将变化的部分与不变的部分分离 我们知道fly和quack在不同的子类中的表现是不同的,因此将这两个方法提取出来并创建一系列的类来实现每一种行为
4.2. 面向接口编程而非面向实现 我们使用一个接口来表示一种行为,例如FlyBehavior接口表示飞翔的行为;QuackBehavior接口表示鸣叫的行为。
这时Duck的子类不负责实现fly和quack的接口,我们会实现一些列的行为类来实现这些接口。接口和类的关系如下图:
这与之前的做法是完全不同的,之前的方式是或者在Duck超类中实现一个行为或者在子类中实现一个行为。在之前的做法中我们都依赖于一种实现,我们的代码就被限定与某一种特定的实现。
在新的设计中,Duck子类将会通过接口(FlyBehavior or QuackBehavior)来使用某一个行为,这样实际行为的实现就不会被限定在Duck的子类中。
接口示例代码:完整代码 
下面的代码中实现了FlyBegavior接口以及两个具体飞翔行为的实现类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public  interface  FlyBegavior   {    public  void  fly ()  ; } class  FlyWithWings  implements   FlyBegavior   {    @Override      public  void  fly ()   {         System.out.println("I can Fly" );     } } class  FlyNoWay  implements  FlyBegavior   {    @Override      public  void  fly ()   {         System.out.println("can't fly" );     } } 
 
完整代码 
下面的代码实现了QuackBehavior接口以及三个鸣叫的实现类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public  interface  QuackBehavior   {    public  void  quack ()  ; } class  Quack  implements  QuackBehavior   {    @Override      public  void  quack ()   {         System.out.println("quack" );     } } class  Squeak  implements  QuackBehavior   {    @Override      public  void  quack ()   {         System.out.println("squeak" );     } } class  MuteQuack  implements  QuackBehavior   {    @Override      public  void  quack ()   {         System.out.println("muteQuack" );     } } 
 
Duck及子类示例代码:完整代码 
在Duck类中定义了两个接口类型的变量,Duck并不负责实现这些接口,只是当子类创建实例时将接口的实现类作为参数传给接口变量,使得程序在执行时动态决定调用哪个具体的实现。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 public  abstract  class  Duck2   {    private  FlyBegavior flyBegavior;     private  QuackBehavior quackBehavior;     public  Duck2 (QuackBehavior quackBehavior, FlyBegavior flyBegavior)   {         this .flyBegavior = flyBegavior;         this .quackBehavior = quackBehavior;     }     public  void  performQuack ()   {         quackBehavior.quack();     }     public  void  swim ()   {         System.out.println("I can siwm" );     }     public  abstract  void  display ()  ;     public  void  performFly ()   {         flyBegavior.fly();     } } class  MallardDuck2  extends  Duck2   {    public  MallardDuck2 ()   {         super (new  Quack(), new  FlyWithWings());     }     @Override      public  void  display ()   {         System.out.println("I'm a real Mallard duck" );     }     public  static  void  main (String[] args)   {         MallardDuck2 m = new  MallardDuck2();         m.display();         m.performFly();         m.performQuack();         m.swim();     } } 
 
5. 练习题