# 算法的封装与切换——策略模式(一)
俗话说:条条大路通罗马。在很多情况下,实现某个目标的途径不止一条,例如我们在外出旅游时可以选择多种不同的出行方式,如骑自行车、坐汽车、坐火车或者坐飞机,可根据实际情况(目的地、旅游预算、旅游时间等)来选择一种最适合的出行方式。在制订旅行计划时,如果目的地较远、时间不多,但不差钱,可以选择坐飞机去旅游;如果目的地虽远、但假期长、且需控制旅游成本时可以选择坐火车或汽车;如果从健康和环保的角度考虑,而且有足够的毅力,自行车游或者徒步旅游也是个不错的选择,大笑。
在软件开发中,我们也常常会遇到类似的情况,实现某一个功能有多条途径,每一条途径对应一种算法,此时我们可以使用一种设计模式来实现灵活地选择解决途径,也能够方便地增加新的解决途径。本章我们将介绍一种为了适应算法灵活性而产生的设计模式——策略模式。
24.1 电影票打折方案
Sunny软件公司为某电影院开发了一套影院售票系统,在该系统中需要为不同类型的用户提供不同的电影票打折方式,具体打折方案如下:
(1) 学生凭学生证可享受票价8折优惠;
(2) 年龄在10周岁及以下的儿童可享受每张票减免10元的优惠(原始票价需大于等于20元);
(3) 影院VIP用户除享受票价半价优惠外还可进行积分,积分累计到一定额度可换取电影院赠送的奖品。
该系统在将来可能还要根据需要引入新的打折方式。
为了实现上述电影票打折功能,Sunny软件公司开发人员设计了一个电影票类MovieTicket,其核心代码片段如下所示:
```
//电影票类
class MovieTicket {
private double price; //电影票价格
private String type; //电影票类型
public void setPrice(double price) {
this.price = price;
}
public void setType(String type) {
this.type = type;
}
public double getPrice() {
return this.calculate();
}
//计算打折之后的票价
public double calculate() {
//学生票折后票价计算
if(this.type.equalsIgnoreCase("student")) {
System.out.println("学生票:");
return this.price * 0.8;
}
//儿童票折后票价计算
else if(this.type.equalsIgnoreCase("children") && this.price >= 20 ) {
System.out.println("儿童票:");
return this.price - 10;
}
//VIP票折后票价计算
else if(this.type.equalsIgnoreCase("vip")) {
System.out.println("VIP票:");
System.out.println("增加积分!");
return this.price * 0.5;
}
else {
return this.price; //如果不满足任何打折要求,则返回原始票价
}
}
}
```
编写如下客户端测试代码:
```
class Client {
public static void main(String args[]) {
MovieTicket mt = new MovieTicket();
double originalPrice = 60.0; //原始票价
double currentPrice; //折后价
mt.setPrice(originalPrice);
System.out.println("原始价为:" + originalPrice);
System.out.println("---------------------------------");
mt.setType("student"); //学生票
currentPrice = mt.getPrice();
System.out.println("折后价为:" + currentPrice);
System.out.println("---------------------------------");
mt.setType("children"); //儿童票
currentPrice = mt.getPrice();
System.out.println("折后价为:" + currentPrice);
}
}
```
编译并运行程序,输出结果如下所示:
```
原始价为:60.0
---------------------------------
学生票:
折后价为:48.0
---------------------------------
儿童票:
折后价为:50.0
```
通过MovieTicket类实现了电影票的折后价计算,该方案解决了电影票打折问题,每一种打折方式都可以称为一种打折算法,更换打折方式只需修改客户端代码中的参数,无须修改已有源代码,但该方案并不是一个完美的解决方案,它至少存在如下三个问题:
(1) MovieTicket类的calculate()方法非常庞大,它包含各种打折算法的实现代码,在代码中出现了较长的if…else…语句,不利于测试和维护。
(2) 增加新的打折算法或者对原有打折算法进行修改时必须修改MovieTicket类的源代码,违反了“开闭原则”,系统的灵活性和可扩展性较差。
(3) 算法的复用性差,如果在另一个系统(如商场销售管理系统)中需要重用某些打折算法,只能通过对源代码进行复制粘贴来重用,无法单独重用其中的某个或某些算法(重用较为麻烦)。
如何解决这三个问题?导致产生这些问题的主要原因在于MovieTicket类职责过重,它将各种打折算法都定义在一个类中,这既不便于算法的重用,也不便于算法的扩展。因此我们需要对MovieTicket类进行重构,将原本庞大的MovieTicket类的职责进行分解,将算法的定义和使用分离,这就是策略模式所要解决的问题,下面将进入策略模式的学习。
- Introduction
- 基础知识
- 设计模式概述
- 从招式与内功谈起——设计模式概述(一)
- 从招式与内功谈起——设计模式概述(二)
- 从招式与内功谈起——设计模式概述(三)
- 面向对象设计原则
- 面向对象设计原则之单一职责原则
- 面向对象设计原则之开闭原则
- 面向对象设计原则之里氏代换原则
- 面向对象设计原则之依赖倒转原则
- 面向对象设计原则之接口隔离原则
- 面向对象设计原则之合成复用原则
- 面向对象设计原则之迪米特法则
- 六个创建型模式
- 简单工厂模式-Simple Factory Pattern
- 工厂三兄弟之简单工厂模式(一)
- 工厂三兄弟之简单工厂模式(二)
- 工厂三兄弟之简单工厂模式(三)
- 工厂三兄弟之简单工厂模式(四)
- 工厂方法模式-Factory Method Pattern
- 工厂三兄弟之工厂方法模式(一)
- 工厂三兄弟之工厂方法模式(二)
- 工厂三兄弟之工厂方法模式(三)
- 工厂三兄弟之工厂方法模式(四)
- 抽象工厂模式-Abstract Factory Pattern
- 工厂三兄弟之抽象工厂模式(一)
- 工厂三兄弟之抽象工厂模式(二)
- 工厂三兄弟之抽象工厂模式(三)
- 工厂三兄弟之抽象工厂模式(四)
- 工厂三兄弟之抽象工厂模式(五)
- 单例模式-Singleton Pattern
- 确保对象的唯一性——单例模式 (一)
- 确保对象的唯一性——单例模式 (二)
- 确保对象的唯一性——单例模式 (三)
- 确保对象的唯一性——单例模式 (四)
- 确保对象的唯一性——单例模式 (五)
- 原型模式-Prototype Pattern
- 对象的克隆——原型模式(一)
- 对象的克隆——原型模式(二)
- 对象的克隆——原型模式(三)
- 对象的克隆——原型模式(四)
- 建造者模式-Builder Pattern
- 复杂对象的组装与创建——建造者模式(一)
- 复杂对象的组装与创建——建造者模式(二)
- 复杂对象的组装与创建——建造者模式(三)
- 七个结构型模式
- 适配器模式-Adapter Pattern
- 不兼容结构的协调——适配器模式(一)
- 不兼容结构的协调——适配器模式(二)
- 不兼容结构的协调——适配器模式(三)
- 不兼容结构的协调——适配器模式(四)
- 桥接模式-Bridge Pattern
- 处理多维度变化——桥接模式(一)
- 处理多维度变化——桥接模式(二)
- 处理多维度变化——桥接模式(三)
- 处理多维度变化——桥接模式(四)
- 组合模式-Composite Pattern
- 树形结构的处理——组合模式(一)
- 树形结构的处理——组合模式(二)
- 树形结构的处理——组合模式(三)
- 树形结构的处理——组合模式(四)
- 树形结构的处理——组合模式(五)
- 装饰模式-Decorator Pattern
- 扩展系统功能——装饰模式(一)
- 扩展系统功能——装饰模式(二)
- 扩展系统功能——装饰模式(三)
- 扩展系统功能——装饰模式(四)
- 外观模式-Facade Pattern
- 深入浅出外观模式(一)
- 深入浅出外观模式(二)
- 深入浅出外观模式(三)
- 享元模式-Flyweight Pattern
- 实现对象的复用——享元模式(一)
- 实现对象的复用——享元模式(二)
- 实现对象的复用——享元模式(三)
- 实现对象的复用——享元模式(四)
- 实现对象的复用——享元模式(五)
- 代理模式-Proxy Pattern
- 设计模式之代理模式(一)
- 设计模式之代理模式(二)
- 设计模式之代理模式(三)
- 设计模式之代理模式(四)
- 十一个行为型模式
- 职责链模式-Chain of Responsibility Pattern
- 请求的链式处理——职责链模式(一)
- 请求的链式处理——职责链模式(二)
- 请求的链式处理——职责链模式(三)
- 请求的链式处理——职责链模式(四)
- 命令模式-Command Pattern
- 请求发送者与接收者解耦——命令模式(一)
- 请求发送者与接收者解耦——命令模式(二)
- 请求发送者与接收者解耦——命令模式(三)
- 请求发送者与接收者解耦——命令模式(四)
- 请求发送者与接收者解耦——命令模式(五)
- 请求发送者与接收者解耦——命令模式(六)
- 解释器模式-Interpreter Pattern
- 自定义语言的实现——解释器模式(一)
- 自定义语言的实现——解释器模式(二)
- 自定义语言的实现——解释器模式(三)
- 自定义语言的实现——解释器模式(四)
- 自定义语言的实现——解释器模式(五)
- 自定义语言的实现——解释器模式(六)
- 迭代器模式-Iterator Pattern
- 遍历聚合对象中的元素——迭代器模式(一)
- 遍历聚合对象中的元素——迭代器模式(二)
- 遍历聚合对象中的元素——迭代器模式(三)
- 遍历聚合对象中的元素——迭代器模式(四)
- 遍历聚合对象中的元素——迭代器模式(五)
- 遍历聚合对象中的元素——迭代器模式(六)
- 中介者模式-Mediator Pattern
- 协调多个对象之间的交互——中介者模式(一)
- 协调多个对象之间的交互——中介者模式(二)
- 协调多个对象之间的交互——中介者模式(三)
- 协调多个对象之间的交互——中介者模式(四)
- 协调多个对象之间的交互——中介者模式(五)
- 备忘录模式-Memento Pattern
- 撤销功能的实现——备忘录模式(一)
- 撤销功能的实现——备忘录模式(二)
- 撤销功能的实现——备忘录模式(三)
- 撤销功能的实现——备忘录模式(四)
- 撤销功能的实现——备忘录模式(五)
- 观察者模式-Observer Pattern
- 对象间的联动——观察者模式(一)
- 对象间的联动——观察者模式(二)
- 对象间的联动——观察者模式(三)
- 对象间的联动——观察者模式(四)
- 对象间的联动——观察者模式(五)
- 对象间的联动——观察者模式(六)
- 状态模式-State Pattern
- 处理对象的多种状态及其相互转换——状态模式(一)
- 处理对象的多种状态及其相互转换——状态模式(二)
- 处理对象的多种状态及其相互转换——状态模式(三)
- 处理对象的多种状态及其相互转换——状态模式(四)
- 处理对象的多种状态及其相互转换——状态模式(五)
- 处理对象的多种状态及其相互转换——状态模式(六)
- 策略模式-Strategy Pattern
- 算法的封装与切换——策略模式(一)
- 算法的封装与切换——策略模式(二)
- 算法的封装与切换——策略模式(三)
- 算法的封装与切换——策略模式(四)
- 模板方法模式-Template Method Pattern
- 模板方法模式深度解析(一)
- 模板方法模式深度解析(二)
- 模板方法模式深度解析(三)
- 访问者模式-Visitor Pattern
- 操作复杂对象结构——访问者模式(一)
- 操作复杂对象结构——访问者模式(二)
- 操作复杂对象结构——访问者模式(三)
- 操作复杂对象结构——访问者模式(四)
- 设计模式趣味学习(复习)
- 设计模式与足球(一)
- 设计模式与足球(二)
- 设计模式与足球(三)
- 设计模式与足球(四)
- 设计模式综合应用实例
- 多人联机射击游戏
- 多人联机射击游戏中的设计模式应用(一)
- 多人联机射击游戏中的设计模式应用(二)
- 数据库同步系统
- 设计模式综合实例分析之数据库同步系统(一)
- 设计模式综合实例分析之数据库同步系统(二)
- 设计模式综合实例分析之数据库同步系统(三)