《Head First 设计模式》之策略模式
前言
昨晚lz在博客园里写了第一篇文章,虽然不是关于技术方面的,但终究算是正式开启了自己写作的生涯。曾经lz加入了一个微信群,是关于英语学习的。这个群里每天早上都会推送一条今日分享,其实就是一句中文和一句英文。当然这推送内容应该说是精挑细选的,会给人传递一些比较正能量的东西。lz觉得这种行为还是比较有意义的,所以,lz会在日后每条文章前面放上这些分享,希望能和广大友人共勉。
最近,lz在学习设计模式,书籍为友人推荐的《Head First 设计模式》,心里有些体会,故将它写下来望能和大家共同交流和学习,写的不好还请大家多多见谅。今天给大家带来的是lz学完策略模式之后的感悟!
策略模式
lz想摘录书中比较经典的话,使用模式最好的方式是:“把模式装进脑子里,然后在你的设计和已有的应用中,寻找何处可以使用它们。”以往是代码复用,现在是经验复用。
先给出模拟鸭子这个应用的最终版本,其UML类图如下:
上面这几个类,接口之间的关系还是很简单,相信大家都能看得懂哈!
再给出具体的类,接口的示例代码如下
封装飞行行为:
1 package xin.yangmj.strategy.behavior.fly; 2 3 /** 4 * 这是飞行行为接口 5 * 6 * @author Eric Yang 7 * @create 2017-10-06 下午1:31 8 **/ 9 public interface FlyBehavior { 10 public void fly(); 11 }
具体的飞行行为实现类:
代表会飞的鸭子:
1 package xin.yangmj.strategy.behavior.fly.impl; 2 3 import xin.yangmj.strategy.behavior.fly.FlyBehavior; 4 5 /** 6 * 这是飞行行为得实现,给“真会”飞的鸭子用... 7 * 8 * @author Eric Yang 9 * @create 2017-10-06 下午1:33 10 **/ 11 public class FlyWithWings implements FlyBehavior { 12 public void fly() { 13 System.out.println("I'm flying!!!"); 14 } 15 }
代表不会飞的鸭子:
1 package xin.yangmj.strategy.behavior.fly.impl; 2 3 import xin.yangmj.strategy.behavior.fly.FlyBehavior; 4 5 /** 6 * 这是飞行行为的实现,给“不会”飞的鸭子用... 7 * 8 * @author Eric Yang 9 * @create 2017-10-06 下午1:36 10 **/ 11 public class FlyNoWay implements FlyBehavior { 12 public void fly() { 13 System.out.println("I can't fly!!!"); 14 } 15 }
拥有火箭动力的鸭子:
1 package xin.yangmj.strategy.behavior.fly.impl; 2 3 import xin.yangmj.strategy.behavior.fly.FlyBehavior; 4 5 /** 6 * 拥有火箭动力的飞行行为 7 * 8 * @author Eric Yang 9 * @create 2017-10-06 下午2:16 10 **/ 11 public class FlyRocketPowered implements FlyBehavior { 12 public void fly() { 13 System.out.println("I'm flying with a rocket!"); 14 } 15 }
以上三个为具体的飞行行为实现类
针对鸭子叫的行为的不同,故也可以分为多种实现,如下;
首先封装呱呱叫行为,也即,抽象为超类
1 package xin.yangmj.strategy.behavior.quack; 2 3 /** 4 * 这是叫的行为接口 5 * 6 * @author Eric Yang 7 * @create 2017-10-06 下午1:40 8 **/ 9 public interface QuackBehavior { 10 public void quack(); 11 }
其次根据呱呱叫个区别有如下三个实现类
1 package xin.yangmj.strategy.behavior.quack.impl; 2 3 import xin.yangmj.strategy.behavior.quack.QuackBehavior; 4 5 /** 6 * 什么都不做 7 * 8 * @author Eric Yang 9 * @create 2017-10-06 下午1:43 10 **/ 11 public class MuteQuack implements QuackBehavior { 12 public void quack() { 13 System.out.println("<< Silence >>"); 14 } 15 }
1 package xin.yangmj.strategy.behavior.quack.impl; 2 3 import xin.yangmj.strategy.behavior.quack.QuackBehavior; 4 5 /** 6 * 鸭子呱呱叫类 7 * 8 * @author Eric Yang 9 * @create 2017-10-06 下午1:41 10 **/ 11 public class Quack implements QuackBehavior { 12 public void quack() { 13 System.out.println("Quack"); 14 } 15 }
1 package xin.yangmj.strategy.behavior.quack.impl; 2 3 import xin.yangmj.strategy.behavior.quack.QuackBehavior; 4 5 /** 6 * 橡皮鸭吱吱叫类 7 * 8 * @author Eric Yang 9 * @create 2017-10-06 下午1:45 10 **/ 11 public class Squeak implements QuackBehavior { 12 public void quack() { 13 System.out.println("Squeak"); 14 } 15 }
现在给出鸭子超类:
1 package xin.yangmj.strategy.duck; 2 3 import xin.yangmj.strategy.behavior.fly.FlyBehavior; 4 import xin.yangmj.strategy.behavior.quack.QuackBehavior; 5 6 /** 7 * 这是鸭子抽象类 8 * 9 * @author Eric Yang 10 * @create 2017-10-06 下午1:29 11 **/ 12 public abstract class Duck { 13 14 // 加上这两个public目的:Duck和其实现类不在同一包下,且Duck在外层包下 15 public FlyBehavior flyBehavior; 16 public QuackBehavior quackBehavior; 17 18 public Duck(){} 19 20 // 委托给行为类 21 public void performFly() { 22 flyBehavior.fly(); 23 } 24 public void performQuack() { 25 quackBehavior.quack(); 26 } 27 28 // 所有鸭子共有的行为 29 public void swim() { 30 System.out.println("All ducks float, even decoys!"); 31 } 32 33 public abstract void display(); 34 35 // 动态设定行为 36 public void setFlyBehavior(FlyBehavior fb) { 37 flyBehavior = fb; 38 } 39 public void setQuackBehavior(QuackBehavior qb) { 40 quackBehavior = qb; 41 } 42 43 }
再给出两个鸭子的具体实现类:
这是绿头鸭子
1 package xin.yangmj.strategy.duck.impl; 2 3 import xin.yangmj.strategy.behavior.fly.impl.FlyWithWings; 4 import xin.yangmj.strategy.behavior.quack.impl.Quack; 5 import xin.yangmj.strategy.duck.Duck; 6 7 /** 8 * 这是绿头鸭子 9 * 10 * @author Eric Yang 11 * @create 2017-10-06 下午2:05 12 **/ 13 public class MallardDuck extends Duck { 14 15 public MallardDuck() { 16 flyBehavior = new FlyWithWings(); 17 quackBehavior = new Quack(); 18 } 19 20 public void display() { 21 System.out.println("I'm a real Mallard duck!"); 22 } 23 }
这是模型鸭子
1 package xin.yangmj.strategy.duck.impl; 2 3 import xin.yangmj.strategy.behavior.fly.impl.FlyNoWay; 4 import xin.yangmj.strategy.behavior.quack.impl.Quack; 5 import xin.yangmj.strategy.duck.Duck; 6 7 /** 8 * 模型鸭子 9 * 10 * @author Eric Yang 11 * @create 2017-10-06 下午2:11 12 **/ 13 public class ModelDuck extends Duck { 14 15 public ModelDuck() { 16 flyBehavior = new FlyNoWay(); 17 quackBehavior = new Quack(); 18 } 19 20 public void display() { 21 System.out.println("I'm a model duck!"); 22 } 23 }
最后给出测试代码:
1 package xin.yangmj.strategy; 2 3 import xin.yangmj.strategy.behavior.fly.impl.FlyRocketPowered; 4 import xin.yangmj.strategy.duck.Duck; 5 import xin.yangmj.strategy.duck.impl.MallardDuck; 6 import xin.yangmj.strategy.duck.impl.ModelDuck; 7 8 /** 9 * 这是整个策略模式的测试类 10 * 11 * @author Eric Yang 12 * @create 2017-10-06 下午2:33 13 **/ 14 public class MiniDuckSimulator { 15 public static void main(String[] args) { 16 Duck mallard = new MallardDuck(); 17 mallard.performFly(); 18 mallard.performQuack(); 19 20 // 动态设定行为 21 Duck model = new ModelDuck(); 22 model.performFly(); 23 // 更改运行行为 24 model.setFlyBehavior(new FlyRocketPowered()); 25 model.performFly(); 26 27 // 哈哈哈,这是测试Git 28 } 29 }
运行结果:
策略模式定义:定义了算法族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。
通过上面的简单介绍可以总结出策略模式运用到的几个设计原则:
1.找出应用中可能需要变化之处,把它们独立出来,不要和哪些不需要变化的代码混在一起。---封装变化,以便以后可以轻易地改动或扩充此部分,而不影响不需要变化的其他部分。在本例中,我们并没有将鸭子的两个行为fly()和quack()放到Duck类里面,这些行为会随着鸭子的不同而改变,让所有的子类都有这些行为是不恰当的,比如,某些鸭子可能不会飞等。所以,我们必须要将这两个行为从Duck类中分离出来,通过建立一组新类来代表每个行为,可以理解为定义中的算法族。这样的设计,可以让飞行和呱呱叫的动作行为被其他的对象复用,因为这些行为已经与鸭子类无关了,而我们也可以新增一些行为,不会影响到既有的行为类,也不会影响“使用”到飞行行为得鸭子类。这么一来,有了继承的“复用”好处,却没有继承所带来的包袱。
2.针对接口编程,而不是针对实现编程。我们可以利用接口代表每个行为,比如说,FlyBehavior与QuackBehavior,而行为得每个实现都将实现其中的一个接口,这样一来,鸭子的子类将使用这两个行为接口所表示的行为,所以实际的“实现”不会被绑死在鸭子的子类中,也即,鸭子类就不再需要知道行为的实现细节。
3.多用组合,少用继承。“有一个”可能比“是一个”更好,本例中,每个鸭子都有飞和叫的行为,将这两个类结合起来使用,就是我们所说的组合。组合和“继承”的区别在于,鸭子的行为不是继承来的,而是和适当的行为对象“组合”来的。