策略模式(Strategy Pattern):
我的理解,将代码中每个变化之处抽出,提炼成一个一个的接口或者抽象类,让这些变化实现接口或继承抽象类成为具体的变化类。再利用多态的功能,可将变化之处用接口或抽象类的对象代替,再用其子类为对象赋值,这样就可以将对象随时更换成具体的变化类。
枯燥的文字描述总是没有实际的例子浅显易懂。
举例:(我是基于notepad++和cmd命令编译运行的JAVA代码)
现在有个鸭子俱乐部,里面有各式各样的鸭子(只有想不到,没有做不到的鸭子)。
我们来一步一步实现这个鸭子类:
1.定义一个抽象鸭子类(Duck):
1 public abstract class Duck{//抽象鸭子类 2 public void fly(){//行为:飞行 3 System.out.println("I'm flying!"); 4 } 5 public void quack(){//行为:呱呱叫 6 System.out.println("Gua Gua!"); 7 } 8 public void swim(){//行为:游泳 9 System.out.println("I'm swimming!"); 10 } 11 }
2.实现一个正常的鸭子类(GreenDuck):
1 public class GreenDuck extends Duck{//GreenDuck直接继承Duck,什么都不做 2 3 public GreenDuck(){ 4 } 5 }
3.测试类(DuckTest):
1 public class DuckTest{//测试类 2 public static void main(String args[]){ 3 GreenDuck greenDuck = new GreenDuck();//实例化一只GreenDuck 4 greenDuck.fly(); 5 greenDuck.quack(); 6 greenDuck.swim(); 7 } 8 }
编译运行+结果:
大功告成!我们去庆祝一下。
正在庆祝,鸭子俱乐部来电话说,我们不仅只要一只GreenDuck,还要RedDuck...不管什么颜色的鸭子都要。你一口应承下来,没问题只是多写几个继承的类而已。
鸭子俱乐部继续说道,我们还要不会飞,不会叫的橡皮鸭,所有颜色的橡皮鸭来一套。你很心虚的答应了,可能今晚要加班加点了,让各种颜色的橡皮鸭继承Duck但是要覆盖其中的fly和quack行为。
鸭子俱乐部没完没了的继续说道,我们还要各种颜色的鸭鸣器,它们只会叫不会飞。现在你心里一定恨死各种颜色这个单词了。
鸭子俱乐部嘴停不下来说各种颜色的...
.......
第二天,哭晕在厕所中。
现在,来看看到底是什么问题导致我们要不停的重复写大量的代码:各种颜色
没错就是这个单词让我们不停地去写各种各样的鸭子实现类(都继承自Duck抽象类),并且有的鸭子不会飞,有的会飞不会叫...
现在有没有感觉到继承带来的恐惧感?我们可以让所有的同类鸭子都继承自Duck抽象类,但是每种鸭子都有自己独特的行为,导致我们要不停地去覆盖Duck抽象类中的行为。
问题找到了。就是继承自抽象类的行为不符合每种鸭子独特的行为导致我们不停地去手动改写或添加行为。我们写这么多的重复代码,没有将代码复用,比如,有的鸭子会飞,有的鸭子会叫,有的鸭子会游泳,有的鸭子不会叫...这么多的行为都写在鸭子实现类中,导致代码冗余,没有将它们复用。
下面让我们的救星:策略模式(Strategy Pattern)登场:
1.首先,fly()和quack()两个方法是一直在变化的,所以我们将这两个变化之处从Duck抽象类中提炼出来变成FlyBehavior接口和QuackBehavior接口,并在Duck抽象类中定义flyBehavior和quackBehavior两个对象。
1 public abstract class Duck{//抽象鸭子类 2 3 /*增加两个接口对象*/ 4 FlyBehavior flyBehavior;//飞行类对象 5 QuackBehavior quackBehavior;//呱呱叫类对象 6 7 public Duck(){ 8 } 9 10 //去除下面两个方法 11 /*public void fly(){//行为:飞行 12 System.out.println("I'm flying!"); 13 } 14 public void quack(){//行为:呱呱叫 15 System.out.println("Gua Gua!"); 16 }*/ 17 18 /*增加下面两个方法,这就是将Duck类的行为委托给两个接口对象实现*/ 19 public void performFly(){//将fly()委托给flyBehavior对象实现 20 flyBehavior.fly(); 21 } 22 public void performQuack(){//将quack()委托给quackBehavior对象实现 23 quackBehavior.quack(); 24 } 25 26 27 public void swim(){//行为:游泳 28 System.out.println("I'm swimming!"); 29 } 30 }
1 public interface FlyBehavior{//从Duck抽象类中抽出的fly()方法变成了FlyBehavior接口 2 public void fly(); 3 }
1 public interface QuackBehavior{//从Duck抽象类中抽出的quack()方法变成了QuackBehavior接口 2 public void quack(); 3 }
其次,将变化具体类分别继承FlyBehavior和QuackBehavior两个接口:
两个飞行具体变化类:
1 public class FlyWithWings implements FlyBehavior{ 2 public void fly(){ 3 System.out.println("I'm flying!"); 4 } 5 }
1 public class FlyNoWay implements FlyBehavior{ 2 public void fly(){ 3 System.out.println("I can't fly!"); 4 } 5 }
两个呱呱叫具体变化类:
1 public class Quack implements QuackBehavior{ 2 public void quack(){ 3 System.out.println("Quack quack!"); 4 } 5 }
1 public class MuteQuack implements QuackBehavior{ 2 public void quack(){ 3 System.out.println("<< Silence >>"); 4 } 5 }
最后,实现一个具体类和测试类:
1 public class GreenDuck extends Duck{//GreenDuck直接继承Duck 2 3 public GreenDuck(){ 4 flyBehavior = new FlyWithWings(); 5 quackBehavior = new Quack(); 6 } 7 8 /*增加一个展示自己是什么鸭子的方法*/ 9 public void display(){ 10 System.out.println("I'm GreenDuck!"); 11 } 12 }
1 public class DuckTest{//测试类 2 public static void main(String args[]){ 3 GreenDuck greenDuck = new GreenDuck();//实例化一只GreenDuck 4 greenDuck.performFly(); 5 greenDuck.performQuack(); 6 greenDuck.swim(); 7 greenDuck.display(); 8 } 9 }
编译运行,结果:
上面的结果,我们可以随时随地的实现不同的具体的鸭子类了,只要在具体的鸭子类中为flyBehavior和quackBehavior实现不同的变化类就好。
2.动态的实现具体变化类的改变:
在Duck类中添加两个新方法(setFlyBehavior(Flybehavior fb)和 setQuackBehavior(QuackBehavior qb) ):
1 public abstract class Duck{//抽象鸭子类 2 3 /*增加两个接口对象*/ 4 FlyBehavior flyBehavior;//飞行类对象 5 QuackBehavior quackBehavior;//呱呱叫类对象 6 7 public Duck(){ 8 } 9 10 //去除下面两个方法 11 /*public void fly(){//行为:飞行 12 System.out.println("I'm flying!"); 13 } 14 public void quack(){//行为:呱呱叫 15 System.out.println("Gua Gua!"); 16 }*/ 17 18 /*增加下面两个方法,这就是将Duck类的行为委托给两个接口对象实现*/ 19 public void performFly(){//将fly()委托给flyBehavior对象实现 20 flyBehavior.fly(); 21 } 22 public void performQuack(){//将quack()委托给quackBehavior对象实现 23 quackBehavior.quack(); 24 } 25 26 /*添加两个新方法,可以动态的改变具体变化类*/ 27 public void setFlyBehavior(FlyBehavior fb){ 28 flyBehavior = fb; 29 } 30 public void setQuackBehavior(QuackBehavior qb){ 31 quackBehaior = qb; 32 } 33 34 35 public void swim(){//行为:游泳 36 System.out.println("I'm swimming!"); 37 } 38 }
改造测试类:
1 public class DuckTest{//测试类 2 public static void main(String args[]){ 3 GreenDuck greenDuck = new GreenDuck();//实例化一只GreenDuck 4 greenDuck.performFly();//一开始GreenDuck会飞 5 greenDuck.performQuack();//一开始GreenDuck会叫 6 7 /*动态改变greenDuck的行为*/ 8 greenDuck.setFlyBehavior(new FlyNoWay()); 9 greenDuck.setQuackBehavior(new MuteQuack()); 10 11 greenDuck.performFly();//现在不会飞了 12 greenDuck.performQuack();//现在不会叫了 13 } 14 }
编译运行,结果:
代码下载网址:
https://github.com/lanshanxiao/Head-First-Design-Pattern/tree/master/1.%E7%AD%96%E7%95%A5%E6%A8%A1%E5%BC%8F(StrategyPattern)%E8%AE%B2%E8%A7%A3%E4%BB%A3%E7%A0%81/%E7%AE%80%E5%8D%95%E5%AE%9E%E7%8E%B0
提炼一下思想:
1.封装
2.“有一个” 比 “是一个” 好(has-a 比 is-a好)
3.多用组合少用继承
4.封装变化
5.针对接口编程,不针对实现编程