####策略模式介绍
  在软件开发中也常常遇到这样的情况:实现某一个功能可以有多种算法或者策略,我们根据实际情况选择不同的算法或者策略来完成该功能。例如,排序算法,可以使用插入排序、归并排序、冒泡排序等。
  针对这种情况,一种方法可以将多种算法写在一个类中,比如将多种排序算法写在一个类中,一个方法对应一个具体的排序算法;也可以将这些算法封装在一个统一的方法中,通过if…else等条件判断语句来选择具体的算法。这两种实现方法我们都可以称为硬编码。然而,多个算法集中在一个类中时,这个类就会变得臃肿,这个类的维护成本就会变高,如果需要一个新的算法,就需要修改封装算法类的源代码。这就违反了OCP原则和单一职责原则。
  如果将这些算法或者策略抽象出来,提供一个统一的接口,不同的算法或者策略有不同的实现类,这样在程序客户端就可以通过注入不同的实现对象来实现算法或者策略的动态替换,这种模式的可扩展性、可维护性也就更高,也就是我们要说的策略模式。
####策略模式的定义
  策略模式定义了一系列算法,并将每一个算法封装起来,而且使它们还可以互相替换。策略模式让算法独立于使用它的客户而独立变化。
####策略模式的使用场景

  • 针对同一类型问题的多种处理方式,仅仅是具体行为有差别时。
  • 需要安全地封装多种同一类型的操作时。
  • 出现同一抽象类有多个子类,而又需要使用if-else或者switch-case来选择具体子类时。
    ####策略模式的UML类图

![这里写图片描述](https://img-blog.csdn.net/20180417165421113?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzE2MjQwMzkz/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)

  • Context—用来操作策略的上下文环境;
  • Stragety—策略的抽象;
  • ConcreteStragetyA、ConcreteStragetyB—具体的策略实现。
    ####策略模式的简单实现
      通常如果一个问题有多个解决方案时,最简单的方式是用if-else或者switch-case方式选择不同的解决方案,但是会带来一系列问题,例如耦合性高、代码臃肿、难以维护等。如果解决方案中包括大量的处理逻辑需要封装,或者处理方式变动较大的时候则就显得混乱、复杂,当需要增加一种方案时还需要修改类中的代码。这就没有遵循开闭原则。
      下面我们以在北京坐公共交通工具的费用计算来演示一个简单示例。
package com.guifa.stragetydemo;public class PriceCalculator {/*** 公交车类型*/private static final int BUS = 1;/*** 地铁类型*/private static final int SUBWAY = 2;public static void main(String[] args) {PriceCalculator priceCalculator = new PriceCalculator();System.out.println("坐16公里的公交车票价为:" + priceCalculator.calculatePrice(16, BUS));System.out.println("坐16公里的地铁票价为:" + priceCalculator.calculatePrice(16, SUBWAY));}/*** 坐公共交通的费用** @param km   距离* @param type 乘坐类型* @return 价格*/private int calculatePrice(int km, int type) {if (type == BUS) {return busPrice(km);} else if (type == SUBWAY) {return subwayPrice(km);} else {return 0;}}/*** 公交车价格:10公里(含)内2元,10公里以上部分,每增加1元可乘坐5公里** @param km 距离* @return 价格*/private int busPrice(int km) {// 超过10公里的距离int extraTotal = km - 10;// 超过的距离是5公里的倍数int extraFactor = extraTotal / 5;// 超过的距离对5公里取余int fraction = extraTotal % 5;// 价格计算int price = 2 + extraFactor * 1;return fraction > 0 ? ++price : price;}/*** 地铁价格:6公里(含)内3元;6公里至12公里(含)4元;12公里至22公里(含)5元;* 22公里至32公里(含)6元;32公里以上部分,每增加1元可乘坐20公里。** @param km 距离* @return 价格*/private int subwayPrice(int km) {if (km <= 6) {return 3;} else if (km > 6 && km <= 12) {return 4;} else if (km > 12 && km <= 22) {return 5;} else if (km > 22 && km <= 32) {return 6;} else {// 其他距离简化为7元return 7;}}
}

PriceCalculator类很明显的问题就是并不是单一职责,首先它承担了计算公交车和地铁乘坐价格的职责;另一个问题就是通过if-else的形式来判断使用哪种计算形式。当我们增加一种出行方式时,如出租车,那么我们就需要在PriceCalculator中增加一个方法来计算出租车出行的价格,并且在calculatePrice(int km, int type)函数中增加一个判断来计算出租车的价格,这时代码就比较混乱了,当价格的计算方式变化时,需要直接修改这个类中的代码,那么很可能有一段代码是其他几个计算方法所共同使用的,就容易引起错误。另外,在增加出行方式时,我们还需要继续添加if-else,它会使得代码变得越来越臃肿,难以维护。为了解决这个问题,可以使用策略模式(当然,也可以把每个计算方法独立成一个函数,让外部调用对应的方法即可,但是这也是另一种耦合的形式,对于可变性较大的算法族来说还是不合适使用这种方式。)
  下面我们对于上述示例用策略模式进行重构。
  首先我们需要定义一个抽象的价格计算接口,这里命名为CalculateStrategy,具体代码如下:

package com.guifa.stragetydemo;public interface CalculateStrategy {/*** 按距离来计算价格** @param km 距离* @return 价格*/int calculatePrice(int km);
}

对于每一种出行方式我们都有哦一个独立的计算策略类,这些策略类都实现了CalculateStrategy接口,例如下面是公交车和地铁的计算策略类:

package com.guifa.stragetydemo;/*** 公交车价格计算策略*/
public class BusStrategy implements CalculateStrategy {/*** 公交车价格:10公里(含)内2元,10公里以上部分,每增加1元可乘坐5公里** @param km 距离* @return 价格*/@Overridepublic int calculatePrice(int km) {// 超过10公里的总距离int extraTotal = km - 10;// 超过的距离是5公里的倍数int extraFactor = extraTotal / 5;// 超过的距离对5公里取余int fraction = extraTotal % 5;// 计算价格int price = 2 + extraFactor * 1;return fraction > 0 ? ++price : price;}
}
package com.guifa.stragetydemo;/*** 地铁价格计算策略*/
public class SubwayStrategy implements CalculateStrategy {/*** 地铁价格:6公里(含)内3元;6公里至12公里(含)4元;12公里至22公里(含)5元;* 22公里至32公里(含)6元;32公里以上部分,每增加1元可乘坐20公里。** @param km 距离* @return 价格*/@Overridepublic int calculatePrice(int km) {if (km <= 6) {return 3;} else if (km > 6 && km <= 12) {return 4;} else if (km > 12 && km <= 22) {return 5;} else if (km > 22 && km <= 32) {return 6;} else {// 其他距离简化为7元return 7;}}
}

我们再创建一个扮演Context角色的类,这里将它命名为TranficCalculator,代码如下:

package com.guifa.stragetydemo;/*** 公交出行价格计算器*/
public class TranficCalculator {CalculateStrategy strategy;public static void main(String[] args) {TranficCalculator calculator = new TranficCalculator();// 设置计算策略calculator.setStrategy(new BusStrategy());// 计算价格System.out.println("公交车乘16公里的价格:" + calculator.strategy(16));}public int strategy(int km) {return strategy.calculatePrice(km);}public void setStrategy(CalculateStrategy strategy) {this.strategy = strategy;}
}

这种方案在隐藏实现的同时,可扩展性变得很强,例如,当我们需要增加出租车的计算策略时,只需要添加一个出租车计算策略类,然后将该策略设置给TranficCalculator,最好直接通过TranficCalculator对象的计算方法即可。示例代码如下:

package com.guifa.stragetydemo;/*** 出租车计算策略*/
public class TaxiStrategy implements CalculateStrategy {@Overridepublic int calculatePrice(int km) {// 价格我们简单计算为公里数*2return km * 2;}
}

将策略注入到TranficCalculator中。

package com.guifa.stragetydemo;/*** 公交出行价格计算器*/
public class TranficCalculator {CalculateStrategy strategy;public static void main(String[] args) {TranficCalculator calculator = new TranficCalculator();// 设置计算策略calculator.setStrategy(new TaxiStrategy());// 计算价格System.out.println("公交车乘16公里的价格:" + calculator.strategy(16));}public int strategy(int km) {return strategy.calculatePrice(km);}public void setStrategy(CalculateStrategy strategy) {this.strategy = strategy;}
}

通过上述示例我们可以清晰地看出二者的区别所在。前者通过if-else来解决问题,虽实现简单,类型层级单一,但暴漏的问题也很明显,即代码臃肿,逻辑复杂,难以升级和维护,没有结构可言;后者则是通过建立抽象,将不同的策略构建成一个具体的策略实现,通过不同的策略实现算法替换。在简化逻辑、结构的同时,增强了系统的可读性、稳定性、可扩展性,这对于较为复杂的业务逻辑显得更为直观,扩展也更为方便。
####总结
  策略模式主要用来分离算法,在相同的行为抽象下有不同的具体实现策略。这个模式很好地演示了开闭原则,也就是定义抽象,注入不同的实现,从而达到很好的可扩展性。
#####优点

  • 结果清晰明了、使用简单直观;
  • 耦合度相对而言较低,扩展方便;
  • 操作封装也更为彻底,数据更为安全。
    #####缺点
  • 随着策略的增加,子类也会变得繁多。

《Android源码设计模式》之策略模式相关推荐

  1. Java设计模式之策略模式与状态模式

    一.策略模式定义 定义:策略模式定义了一系列的算法,并将每一个算法封装起来,而且使他们之间可以相互替换,策略模式可以在不影响客户端的情况下发生变化. 好了,定义看看就完了,我知道你很烦看定义. 二.策 ...

  2. 换个姿势学设计模式:策略模式

    点击上方蓝色"程序猿DD",选择"设为星标" 回复"资源"获取独家整理的学习资料! 来源:公众号「闻人的技术博客」 前言 前段时间,接到一个 ...

  3. 研磨设计模式之 策略模式--转

    http://www.uml.org.cn/sjms/201009092.asp 研磨设计模式之 策略模式   2010-09-09 作者:云飞龙行 来源:云飞龙行的blog   先感谢众多朋友的支持 ...

  4. 设计模式:策略模式(Strategy)

    定   义:它定义了算法家族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化, 不会影响到使用算法的客户. 示例:商场收银系统,实现正常收费.满300返100.打8折.......等不同收费 ...

  5. C++设计模式之策略模式(Strategy)

    Strategy策略模式 作用:定义了算法家族,分别封装起来,让他们之间可以互相替换,此模式让算法的变化,不会影响到使用算法的客户. UML图: 代码实现 #include <iostream& ...

  6. python策略模式包含角色_详解Python设计模式之策略模式

    虽然设计模式与语言无关,但这并不意味着每一个模式都能在每一门语言中使用.<设计模式:可复用面向对象软件的基础>一书中有 23 个模式,其中有 16 个在动态语言中"不见了,或者简 ...

  7. 一篇博客读懂设计模式之-----策略模式

    设计模式之策略模式 在策略模式中,我们创建表示各种策略的对象和一个行为随着策略对象改变而改变的对象 定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换. 主要解决:在有多种算法相似的情况下 ...

  8. 面向对象设计模式之策略模式

    面向对象设计模式之策略模式 1.策略模式定义了算法族,分别封装起来,让他们之间可以互相替换,此模式让算法的变化独立于使用算法的客户 2.抽象鸭子类,鸭子飞行行为在此处类似于算法族 1 package ...

  9. java策略模式详解_Java经典设计模式之策略模式原理与用法详解

    本文实例讲述了Java经典设计模式之策略模式.分享给大家供大家参考,具体如下: 策略模式指:策略模式指将程序中可变部分抽象分离成一系列的算法,并将每一个算法封装起来,而且使它们还可以相互替换.策略模式 ...

  10. 策略设计模式_设计模式之策略模式总结

    再上一篇文章<设计模式之策略模式>中,我们通过模拟鸭子项目,了解了什么是策略模式,怎么使用策略模式.本文将通过鸭子项目的学习,对策略模式进行总结. 策略模式: 分别封装行为接口,实现算法族 ...

最新文章

  1. Access violation at address 0x77f96c94
  2. 配置Exchange 2007边缘同步
  3. bootstrap基础
  4. php压制错误的代码,为什么要压制PHP错误?
  5. HDU2504 又见GCD
  6. java计算时间差_JAVA并发编程三大Bug源头(可见性、原子性、有序性),彻底弄懂...
  7. Linux开机启动过程(12):start_kernel()->还是setup_arch
  8. 如何写论文?看下这份《科研论文撰写策略》为你指点一二
  9. matlab 中for 的控制表达式用数组,循环指数可以为向量
  10. Mac安装Qt出现错误Could not resolve SDK Path for ‘macosx‘
  11. 小学生c语言编程入门教程_学生编程语言
  12. 负数的二进制转换方法
  13. 平安城市视频监控运维解决方案
  14. 百度开放平台四部分:应用、数据、知道、地图
  15. failed to locate @import file common/stylus/variable.styl
  16. 开会的五有五不四框架
  17. PeopleSoft开发:创建查询QUERY
  18. IE 获取不到元素 img标签层级默认变高
  19. Python基础——PyCharm版本——第八章、文件I/O(核心2——JSON序列化与反序列化——超重点)
  20. 最快的计算机操作,自学电脑操作怎样比较快?

热门文章

  1. 实现 PV、UV、IP 日统计
  2. qt编译出错 /usr/bin/ld 找不到 -lGL cannot find -lGL
  3. 讯飞AIUI ubuntu linux使用
  4. 603. Consecutive Available Seats
  5. FCC 中级算法题 罗马数字转换器
  6. Consider injecting the bean as one of its interfaces or forcing the use of CGLib-based proxies 问题解决
  7. 3D产品建模为3D产品展示打下基础
  8. 冈萨雷斯《数字图像处理》学习笔记(五)图像复原
  9. 数学规划模型(二):线性规划模型
  10. 通信端口感叹号_WAN微型端口有黄色感叹号无线网络连接不上