### 一、打折的煩惱
有一家賣書的網站想做一套結算系統,其中的一部分就是計算書的價格,這家網站上的書基本上都有優惠,而且不同種類的書優惠不同,比如漫畫書打9折,小說打6折等等,他們剛開始的設計是這樣的。
方案一:在客戶端進行判斷
~~~
if(book is comic)
price*=0.9;
else if(book is novel)
price*=0.6;
~~~
看起來好像也沒什么問題,但是當我們的書種類非常的時候,客戶端的代碼就會顯得非常臃腫,并且每當我們添加一種新的書就要添加一個if-else語句,如果這本書下架了就要刪除相應的語句,顯得很麻煩。
方案二:
我們可以定義一個Book的抽象類,每種書都繼承自這個類并且每個類中都實現自己的計算價格的方法,這樣就避免了客戶端的臃腫,但是這會將算法和書的實現耦合到一塊,如果我們想要修改折扣的幅度,那么對應的書的類就要修改,不符合“開-閉”原則。
方案三:
我們將每一種計算價格的方法都封裝成一個策略類,在客戶端中可以動態的設定,這種書使用哪種策略,當有新的打折方式時,我們可以增加一個新的策略類,而不需要去修改書的代碼,將書和算法完全分離開來,減小了耦合度,符合“開-閉”原則。
### 二、策略模式的定義和結構
上面的第三種方案就是我們說的策略模式。策略模式將每一個算法封裝起來,并讓它們可以相互替換。策略模式讓算法獨立于使用它的客戶而變化。

策略模式涉及到三個角色:
環境(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(一)類圖以及類間關系(泛化 、實現、依賴、關聯、聚合、組合)
- 設計模式之橋梁模式和策略模式的區別