Head First设计模式<一>:策略模式
前言:最近在看Head First设计模式,有所收获记录总结一下,今天说一下策略模式解决的问题。
<1> 策略模式是根据外部不同的需求选择不同的策略来解决不同需求的问题。
<2>策略模式更适用于算法,加减,计算公式,和应对与一种产品后期可能会出现多变的需求情况下使用。
<3>策略模式将可变的需求分组成不同的对象或者接口,来进行策略组的不同实现。
<4>通过策略分组,使得每个分组有不同的策略应对这个分组行为产生的不同需求。
下面我来设计一个策略需求。
1 需求:
以鸭子为例,有小黄鸭,黑鸭子,橡皮鸭子,木头鸭子等...
2 功能:
小黄鸭:会飞,会叫,会游泳,会烧烤...
黑鸭子:会飞,会叫,会游泳,会烧烤...
橡皮鸭子:会叫,会游泳...
木头鸭子:会游泳...
策略模式设计(使用StartUML的ER随手画的不规范凑合看):
可以看到,我们针对不同的鸭子,我们对鸭子赋予的功能策略是不同的,当然如果你说木头鸭子可以烧烤,请出门右转,自己找烧烤店。。。
3 实例讲解:废话不多说举一个小黄鸭的例子,上代码
步骤一:定义鸭子抽象超类,策略分组的接口,公共的方法
1 package com.alibaba.strategy; 2 3 /** 4 * 鸭子类 -- 策略模式超类 5 * 6 * @author Mr.Tk 7 * @date 2019/3/09 16:25 8 */ 9 public abstract class Duck { 10 11 /** 12 * 飞行行为 13 */ 14 FlyBehavior flyBehavior; 15 16 /** 17 * 叫声行为 18 */ 19 QuackBehavior quackBehavior; 20 21 public Duck() { 22 23 } 24 25 /** 26 * 鸭子的颜色 27 */ 28 public abstract void display(); 29 30 /** 31 * 委托执行飞行方法 32 */ 33 public void performFly() { 34 flyBehavior.fly(); 35 } 36 37 /** 38 * 委托执行叫声方法 39 */ 40 public void performQuack() { 41 quackBehavior.quack(); 42 } 43 44 /** 45 * 游泳方法 46 */ 47 public void swim() { 48 System.out.println("鸭子在游泳,请勿打扰。烧烤请左转..."); 49 } 50 51 }
步骤二:创建鸭子的行为接口,策略分组
package com.alibaba.strategy; /** * 鸭子的飞行,行为接口 * * @author Mr.Tk * @date 2019/3/09 16:28 */ public interface FlyBehavior { void fly(); }
package com.alibaba.strategy; /** * 鸭子叫声,行为接口 * * @author Mr.Tk * @date 2019/3/09 16:29 */ public interface QuackBehavior { void quack(); }
步骤三:策略组实现的策略,应对每个策略分组下面有不同的策略去应对不同的需求
1 package com.alibaba.strategy.impl; 2 3 import com.alibaba.strategy.FlyBehavior; 4 5 /** 6 * 展翅高飞 7 * 8 * @author Mr.Tk 9 * @date 2019/3/09 16:29 10 */ 11 public class FlyWithwings implements FlyBehavior { 12 @Override 13 public void fly() { 14 System.out.println("展翅高飞的鸭子。。。"); 15 } 16 }
package com.alibaba.strategy.impl; import com.alibaba.strategy.FlyBehavior; /** * 不会飞行的鸭子 * * @author Mr.Tk * @date 2019/3/09 16:29 */ public class FlyNoWay implements FlyBehavior { @Override public void fly() { System.out.println("本鸭不会飞,告辞。。。"); } }
1 package com.alibaba.strategy.impl; 2 3 import com.alibaba.strategy.QuackBehavior; 4 /** 5 * 鸭子叫 6 * 7 * @author Mr.Tk 8 * @date 2019/3/09 16:46 9 */ 10 public class Quack implements QuackBehavior { 11 @Override 12 public void quack() { 13 System.out.println("鸭子叫声。。。"); 14 } 15 }
1 package com.alibaba.strategy.impl; 2 3 import com.alibaba.strategy.QuackBehavior; 4 /** 5 * 不会叫的鸭子 6 * 7 * @author Mr.Tk 8 * @date 2019/3/09 16:46 9 */ 10 public class MuteQuack implements QuackBehavior { 11 @Override 12 public void quack() { 13 System.out.println("鸭子不会叫。。。"); 14 } 15 }
1 package com.alibaba.strategy.impl; 2 3 import com.alibaba.strategy.QuackBehavior; 4 5 /** 6 * 吱吱叫的鸭子 7 * 8 * @author Mr.Tk 9 * @date 2019/3/09 16:46 10 */ 11 public class Squeak implements QuackBehavior { 12 @Override 13 public void quack() { 14 System.out.println("吱吱叫的鸭子。。。"); 15 } 16 }
步骤四:我们来实现一个鸭子的独有策略
1 package com.alibaba.strategy; 2 3 import com.alibaba.strategy.impl.FlyWithwings; 4 import com.alibaba.strategy.impl.Quack; 5 6 /** 7 * 小黄鸭 8 * 9 * @author Mr.Tk 10 * @date 2019/3/09 16:51 11 */ 12 public class YellowDuck extends Duck { 13 14 public YellowDuck() { 15 quackBehavior = new Quack(); 16 flyBehavior = new FlyWithwings(); 17 } 18 19 @Override 20 public void display() { 21 System.out.println("烧烤小黄鸭。。。"); 22 } 23 }
步骤五:Test测试一下鸭子是否实现小黄鸭的功能了
1 package com.alibaba.strategy; 2 3 public class Test { 4 5 public static void main(String[] args) { 6 //创建小黄鸭 7 Duck yellowDuck = new YellowDuck(); 8 //飞起来 9 yellowDuck.performFly(); 10 //叫起来 11 yellowDuck.performQuack(); 12 //游泳 13 yellowDuck.swim(); 14 //展示 15 yellowDuck.display(); 16 } 17 }
console运行结果:
展翅高飞的鸭子。。。 鸭子叫声。。。 鸭子在游泳,请勿打扰。烧烤请左转... 烧烤小黄鸭。。。 Process finished with exit code 0
4 说一下优缺点
4.1 策略接口可以相互结合使用,可以随意切换。
4.2 符合程序设计的“开闭原则”,如果该策略组下需要不同的策略,我们新增一个策略即可,不必改动原有代码。
4.3 使用策略模式可以减少我们代码中因为业务增加,而产生大量的 if...else..从而使服务层以为业务的增加而变得臃肿起来。
5 缺点
5.1 我们需要了解所有的策略类,来决定到底使用哪一个策略组下的那个策略。
5.2 随着业务的增加,可能每个策略组下的策略会越来越多。
解决方案:我的下一篇博客,会介绍享元模式,此篇博客就不说策略模式以外的东西了,有兴趣的可以自行百度,皮一下 -.-。
6 项目应用场景的例子
一个地区的仲裁(法院)机构,会有不同的案件诉讼费用的计算方法。比如假设上海仲裁他的计算方式是,费用 = 受理费*当事人是否本地户籍(是20% / 不是 30%);而北京仲裁委就是另一种计算方式。还有其他的省下面有大大小小很多市,每个市的机构计算费用方法不一样,我们使用策略模式进行省的分组,每个省下的每个市有不同的计费策略。这里举的一个例子只是我在项目中遇到的业务场景,当然策略模式使用在算法上面的业务比较多,针对不同的业务去使用设计模式来帮助我们更好的完成工作是每个"攻城狮"的基本功,希望大家可以get不同的思路,有所收获。
矛盾说:我从来不梦想,我只是在努力认识现实。
戏剧家洪深说:我的梦想是明年吃苦的能力比今年更强。
鲁迅说:人生最大的痛苦是梦醒了无路可走。
苏格拉底说:人类的幸福和欢乐在于奋斗,而最有价值的是为理想而奋斗。