# 算法的封裝與切換——策略模式(一)
俗話說:條條大路通羅馬。在很多情況下,實現某個目標的途徑不止一條,例如我們在外出旅游時可以選擇多種不同的出行方式,如騎自行車、坐汽車、坐火車或者坐飛機,可根據實際情況(目的地、旅游預算、旅游時間等)來選擇一種最適合的出行方式。在制訂旅行計劃時,如果目的地較遠、時間不多,但不差錢,可以選擇坐飛機去旅游;如果目的地雖遠、但假期長、且需控制旅游成本時可以選擇坐火車或汽車;如果從健康和環保的角度考慮,而且有足夠的毅力,自行車游或者徒步旅游也是個不錯的選擇,大笑。
在軟件開發中,我們也常常會遇到類似的情況,實現某一個功能有多條途徑,每一條途徑對應一種算法,此時我們可以使用一種設計模式來實現靈活地選擇解決途徑,也能夠方便地增加新的解決途徑。本章我們將介紹一種為了適應算法靈活性而產生的設計模式——策略模式。
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
- 操作復雜對象結構——訪問者模式(一)
- 操作復雜對象結構——訪問者模式(二)
- 操作復雜對象結構——訪問者模式(三)
- 操作復雜對象結構——訪問者模式(四)
- 設計模式趣味學習(復習)
- 設計模式與足球(一)
- 設計模式與足球(二)
- 設計模式與足球(三)
- 設計模式與足球(四)
- 設計模式綜合應用實例
- 多人聯機射擊游戲
- 多人聯機射擊游戲中的設計模式應用(一)
- 多人聯機射擊游戲中的設計模式應用(二)
- 數據庫同步系統
- 設計模式綜合實例分析之數據庫同步系統(一)
- 設計模式綜合實例分析之數據庫同步系統(二)
- 設計模式綜合實例分析之數據庫同步系統(三)