### 一、打折的烦恼
有一家卖书的网站想做一套结算系统,其中的一部分就是计算书的价格,这家网站上的书基本上都有优惠,而且不同种类的书优惠不同,比如漫画书打9折,小说打6折等等,他们刚开始的设计是这样的。
方案一:在客户端进行判断
~~~
if(book is comic)
price*=0.9;
else if(book is novel)
price*=0.6;
~~~
看起来好像也没什么问题,但是当我们的书种类非常的时候,客户端的代码就会显得非常臃肿,并且每当我们添加一种新的书就要添加一个if-else语句,如果这本书下架了就要删除相应的语句,显得很麻烦。
方案二:
我们可以定义一个Book的抽象类,每种书都继承自这个类并且每个类中都实现自己的计算价格的方法,这样就避免了客户端的臃肿,但是这会将算法和书的实现耦合到一块,如果我们想要修改折扣的幅度,那么对应的书的类就要修改,不符合“开-闭”原则。
方案三:
我们将每一种计算价格的方法都封装成一个策略类,在客户端中可以动态的设定,这种书使用哪种策略,当有新的打折方式时,我们可以增加一个新的策略类,而不需要去修改书的代码,将书和算法完全分离开来,减小了耦合度,符合“开-闭”原则。
### 二、策略模式的定义和结构
上面的第三种方案就是我们说的策略模式。策略模式将每一个算法封装起来,并让它们可以相互替换。策略模式让算法独立于使用它的客户而变化。
![这里写图片描述](https://box.kancloud.cn/2016-04-21_571890ecc9f8f.jpg "")
策略模式涉及到三个角色:
环境(Context)角色:这个是要使用策略(算法)的类,持有一个Strategy类的引用。
抽象策略角色(Strategy):定义策略类应该有的接口。
具体策略角色(Concrete):具体实现相关的算法。
### 三、模式用例
~~~
package com.designpattern.adapter.strategy;
/**
* 各种书的抽象类
* @author 98583
*
*/
public abstract class Book {
/**
* 原价
*/
protected double basePrice = 0.0;
/**
* 策略的引用
*/
protected Strategy strategy;
public Book(double basePrice){
this.basePrice = basePrice;
}
/**
* 设置策略
* @param strategy
*/
public void setStrategy(Strategy strategy){
this.strategy = strategy;
}
/**
* 利用策略的统一接口实现获取书的应付价格
* @return
*/
public double getPrice(){
return strategy.getDiscount(basePrice);
}
/**
* 抽象的展示方法,每类书都有自己的实现方式
*/
public abstract void show();
}
~~~
~~~
package com.designpattern.adapter.strategy;
public class Comic extends Book{
public Comic(double basePrice) {
super(basePrice);
}
public void show() {
System.out.println("The Comic Book is "+getPrice());
}
}
~~~
~~~
package com.designpattern.adapter.strategy;
public abstract class Strategy {
public abstract double getDiscount(double basePrice);
}
~~~
~~~
package com.designpattern.adapter.strategy;
public class NovelStrategy extends Strategy{
public double getDiscount(double basePrice) {
return 0.6*basePrice;
}
}
~~~
~~~
package com.designpattern.adapter.strategy;
public class Client {
public static void main(String[] args) {
Book book = new Comic(12.3);
Strategy strategy = new ComicStrategy();
//设置采用何种策略
book.setStrategy(strategy);
book.show();
}
}
~~~
小说类的代码和漫画类的代码类似,不再贴出,最后会附上源码。
### 四、策略模式的优缺点
**优点:**
- 策略模式提供了对“开闭原则”的完美支持,用户可以在不修改原有系统的基础上选择算法或行为,也可以灵活地增加新的算法或行为。
- 策略模式提供了管理相关的算法族的办法。
- 策略模式提供了可以替换继承关系的办法。
- 使用策略模式可以避免使用多重条件转移语句。
**缺点:**
- 客户端必须知道所有的策略类,并自行决定使用哪一个策略类。
- 策略模式将造成产生很多策略类,可以通过使用享元模式在一定程度上减少对象的数量。
### 五、策略模式的使用情况
1、如果在一个系统里面有许多类,它们之间的区别仅在于它们的行为,那么使用策略模式可以动态地让一个对象在许多行为中选择一种行为。
一个系统需要动态地在几种算法中选择一种。
2、如果一个对象有很多的行为,如果不用恰当的模式,这些行为就只好使用多重的条件选择语句来实现。
3、不希望客户端知道复杂的、与算法相关的数据结构,在具体策略类中封装算法和相关的数据结构,提高算法的保密性与安全性。
源码下载:[http://download.csdn.net/detail/xingjiarong/9324481](http://download.csdn.net/detail/xingjiarong/9324481)
- 前言
- 设计原则(一)"开-闭"原则(OCP)
- 设计原则(二)里氏替换原则(LSP)
- 设计原则(三)组合复用原则
- 设计原则(四)依赖倒置原则(DIP)
- 设计模式(一)简单工厂模式
- 设计模式(二)工厂方法模式
- 设计模式(三)抽象工厂模式
- 设计模式(四)单例模式
- 设计模式(五)创建者模式(Builder)
- 设计模式(六)原型模式
- 设计模式(七)门面模式(Facade Pattern 外观模式)
- 设计模式(八)桥梁模式(Bridge)
- 设计模式(九)装饰模式(Decorator)
- 设计模式(十)适配器模式
- 设计模式(十一)策略模式
- 设计模式(十二)责任链模式
- 设计模式之UML(一)类图以及类间关系(泛化 、实现、依赖、关联、聚合、组合)
- 设计模式之桥梁模式和策略模式的区别