Design Pattern
1、概述
2、创建型模式
2.1 单例模式
2.2 工厂模式
3、结构型模式
3.1 代理模式
3.2 装饰器模式
4、行为型模式
4.1 策略模式
-
+
游客
注册
登录
策略模式
## 1 模式动机 1. 完成一项任务,往往可以有多种不同的方式,每一种方式称为一个策略,我们可以根据环境或者条件的不同选择不同的策略来完成该项任务。 2. 在软件开发中也常常遇到类似的情况,实现某一个功能有多个路径,此时可以使用一种设计模式来使得系统可以灵活地选择解决路径,也能够方便地增加新的解决路径。 3. 在软件系统中,有许多算法可以实现某一功能,如查找、排序等,一种常用的方法是硬编码在一个类中,如需要提供多种查找方法,可以将这些算法写到一个类中,在该类中提供多个方法,每一个方法对应一个具体的查找算法,当然也可以将这些查找算法封装在一个统一的方法中,通过`if...else...` 等条件判断语句来进行选择,这两种实现方法我们都称之为硬编码,如果需要增加一种新的查找算法,需要修改封装算法类的源代码,更换查找算法,也需要修改客户端调用代码,在这个算法类中封装了大量查找算法,该类代码将较复杂,维护较为困难。 4. 除了提供专门的查找算法类之外,还可以在客户端程序中直接包含算法代码,这种做法更不可取,将导致客户端程序庞大而且难以维护,如果存在大量可供选择的算法时,问题将变得更加严重。 5. 为了解决这些问题,可以定义一些独立的类来封装不同的算法,每一个类封装一个具体的算法,在这里,**每一个封装算法的类我们都可以称之为策略**(Strategy),为了**保证这些策略的一致性**,一般会**用一个抽象的策略类来做算法的定义**,而**具体每种算法则对应于一个具体策略类**。 ## 2 模式定义 1. 策略模式(Strategy Pattern)是指**定义一系列算法**,**将每一个算法封装起来**,并**让他们可以相互替换**。 2. 策略模式**让算法独立于使用他的客户而变化**,也称为**政策模式**(Policy Pattern)。 ## 3 模式结构 策略模式包含如下角色: 1. 「**抽象策略**(Strategy)」角色:**是「具体策略」的父类**,**定义所有支持的算法的公共接口**。 2. 「**具体策略**(ConcreteStrategy)」角色:**封装了具体的算法或行为**。 3. 「**环境**(Context)」角色:**用一个「具体策略」来配置**,**维护一个对抽象策略对象的引用**。![../_images/Strategy.jpg](/media/202108/2021-08-27_1622310.4911313623224278.png) ## 4 使用步骤 策略模式的使用主要包含四步: 1. **创建「抽象策略」类**,并**定义所有支持的算法的公共接口**。 2. **创建「具体策略」类**,并**定义具体的算法实现**。 3. **创建「环境」类**,**通过构造方法**,**传入具体的策略参数**,**并根据具体的策略对象来调用其算法**。 4. **外界通过调用「环境」类的方法**,**通过传入不同参数来实例化不同的策略**,**从而得到不同的结果**。![](/media/202108/2021-08-27_1626290.5570861613559134.png) ## 5 示例 下面将通过一个商场的收银系统为例进行说明,商场收银系统需要能够处理正常收费、商品打折和节假日满减等各种活动,这里**正常收费**、**打折和满减是该系统需要实现的具体算法**,他们都**共用一个收费接口**。 ### 5.1 创建抽象策略类 创建 `CashStrategy` 的抽象策略类,并定义所支持的算法的公共接口 `acceptCash()`: ```java abstract class CashStrategy { /** * 收费 * @param money 金额 * @return 最终收费金额 */ public abstract double acceptCash(double money); } ``` ### 5.2 创建具体策略类 创建具体策略类(`CashNormalStrategy`、`CashReturnStrategy`、`CashRebateStrategy` 等),来继承 `CashStrategy` 的抽象策略类,并实现 `acceptCash()` 方法,来定义具体的算法: ```java public class CashNormalStrategy extends CashStrategy{ /** * 收费 * @param money 金额 * @return 最终收费金额 */ @Override public double acceptCash(double money) { System.out.println(String.format("正常收费,原金额 %s.", money)); return money; } } ``` ```java public class CashReturnStrategy extends CashStrategy{ private double moneyCondition = 0.0; private double moneyReturn = 0.0; public CashReturnStrategy(final double moneyCondition, final double moneyReturn) { this.moneyCondition = moneyCondition; this.moneyReturn = moneyReturn; } /** * 收费 * @param money 金额 * @return 最终收费金额 */ @Override public double acceptCash(double money) { double res = money; if (money >= moneyCondition) { res = money - Math.floor(money / moneyCondition) * moneyReturn; } System.out.println(String.format("满 %s 返 %s,原金额 %s.", moneyCondition, moneyReturn, money)); return res; } } ``` ```java public class CashRebateStrategy extends CashStrategy{ private double moneyRebate = 1.0; public CashRebateStrategy(final double moneyRebate) { this.moneyRebate = moneyRebate; } /** * 收费 * @param money 金额 * @return 最终收费金额 */ @Override public double acceptCash(double money) { double res = money * moneyRebate; System.out.println(String.format("打折,折扣比率为 %s,原金额 %s.", moneyRebate, money)); return res; } } ``` ### 5.3 创建环境类 通过构造方法,传入具体的收费策略,并根据具体的策略对象来调用其算法,得到不同的计算结果: ```java public class CashContext { private CashStrategy cashStrategy; public CashContext(String type) { switch (type) { case "0": cashStrategy = new CashNormalStrategy();break; case "满百返百": cashStrategy = new CashReturnStrategy(300.0, 100.0); break; case "8 折": cashStrategy = new CashRebateStrategy(0.8); break; } } /** * 获取最终的收费金额 * @param money 原始金额 * @return 最终的收费金额 */ public double getResult(double money) { return cashStrategy.acceptCash(money); } } ``` ### 5.4 调用环境类的方法 在主函数中(客户端),只需调用环境类,传入收费类型,就可以得到收费的结果: ```java public class Client { public static void main(String[] args) throws InterruptedException { Scanner scanner = new Scanner(System.in); String type = null; double money = 0.0, res = 0.0; System.out.println("Please input the discount(0/满百返百/8 折/结束): "); type = scanner.nextLine(); while (!type.equals("结束")) { CashContext cashContext = new CashContext(type); System.out.println("Please input money: "); money = scanner.nextDouble(); res = cashContext.getResult(money); System.out.println(String.format("最终消费金额为 %s", res)); System.out.println("Please input the discount(0/满百返百/8 折/结束): "); type = scanner.nextLine(); type = scanner.nextLine(); } } } ``` ## 6 优缺点 ### 6.1 优点 1. 策略模式**提供了对开闭原则的完美支持**,**用户可以在不修改原有系统的基础上选择算法或行为**,**也可以灵活地增加新的算法或行为**。 2. 策略模式**提供了管理相关的算法族的办法**。 3. 策略模式**提供了可以替换继承关系的办法**。 4. 使用策略模式**可以避免使用多重条件转移语句**。 ### 6.2 缺点 1. 客户端**必须知道所有的策略类**,并**自行决定使用哪一个策略类**。 2. 策略模式将造成**产生很多策略类**,**可以通过享元模式在一定程度上减少对象的数量**。 ## 7 适用环境 在以下情况下可以使用策略模式: 1. 如果**在一个系统里面有很多类**,**他们之间的区别仅在于他们的行为**,那么使用策略模式**可以动态地让一个对象在许多行为中选择一种行为**。 2. **一个系统需要动态地在几种算法中选择一种**。 3. 如果**一个对象有很多的行为**,如果**不用恰当的模式**,**这些行为就只好使用多重的条件选择语句来实现**。 4. **不希望客户端知道复杂的**、**与算法相关的数据结构**,**在具体策略类中封装算法和相关的数据结构**,**提高算法的保密性与安全性**。 ## 参考文献 1. [设计模式面试题(总结最全面的面试题!!!)](https://juejin.cn/post/6844904125721772039)。 2. [5. 策略模式](https://design-patterns.readthedocs.io/zh_CN/latest/behavioral_patterns/strategy.html)。 3. [策略模式](https://refactoringguru.cn/design-patterns/strategy)。 4. [大话设计模式 | 2. 策略模式](https://xie.infoq.cn/article/05513a9ca11f3850759bf275e)。
ricear
2021年8月28日 10:05
©
BY-NC-ND(4.0)
转发文档
收藏文档
上一篇
下一篇
手机扫码
复制链接
手机扫一扫转发分享
复制链接
Markdown文件
分享
链接
类型
密码
更新密码