簡說設計模式——策略模式


一、什么是策略模式

  策略這個詞應該怎么理解,打個比方說,我們出門的時候會選擇不同的出行方式,比如騎自行車、坐公交、坐火車、坐飛機、坐火箭等等,這些出行方式,每一種都是一個策略。

  再比如我們去逛商場,商場現在正在搞活動,有打折的、有滿減的、有返利的等等,其實不管商場如何進行促銷,說到底都是一些算法,這些算法本身只是一種策略,並且這些算法是隨時都可能互相替換的,比如針對同一件商品,今天打八折、明天滿100減30,這些策略間是可以互換的。

  策略模式(Strategy),定義了一組算法,將每個算法都封裝起來,並且使它們之間可以互換。UML結構圖如下:

  其中,Context是上下文,用一個ConcreteStrategy來配置,維護一個對Strategy對象的引用;Strategy是策略類,用於定義所有支持算法的公共接口;ConcreteStrategy是具體策略類,封裝了具體的算法或行為,繼承於Strategy。

  1. Context上下文

  Context上下文角色,也叫Context封裝角色,起承上啟下的作用,屏蔽高層模塊對策略、算法的直接訪問,封裝可能存在的變化。

 1 public class Context {
 2     
 3     Strategy strategy;
 4     
 5     public Context(Strategy strategy) {
 6         this.strategy = strategy;
 7     }
 8     
 9     //上下文接口
10     public void contextInterface() {
11         strategy.algorithmInterface();
12     }
13 
14 }

  2. 策略角色

  抽象策略角色,是對策略、算法家族的抽象,通常為接口,定義每個策略或算法必須具有的方法和屬性。algorithm是“運算法則”的意思。

1 public abstract class Strategy {
2     
3     //算法方法
4     public abstract void algorithmInterface();
5 
6 }

  3. 具體策略角色

  用於實現抽象策略中的操作,即實現具體的算法,下方用print代替。測試類共3個ConcreteStrategy,其它兩個類與ConcreteStrategyA同理,就不再贅述了。

1 public class ConcreteStrategyA extends Strategy {
2 
3     @Override
4     public void algorithmInterface() {
5         System.out.println("算法A實現");
6     }
7 
8 }

  4. Client客戶端

  下面依次更換策略,測試一下策略模式。

 1 public class Client {
 2     
 3     public static void main(String[] args) {
 4         Context context;
 5         
 6         context = new Context(new ConcreteStrategyA());
 7         context.contextInterface();
 8         
 9         context = new Context(new ConcreteStrategyB());
10         context.contextInterface();
11         
12         context = new Context(new ConcreteStrategyC());
13         context.contextInterface();
14     }
15 
16 }

  運行結果如下:

  

二、策略模式的應用

  1. 何時使用

  • 一個系統有許多類,而區分它們的只是他們直接的行為時

  2. 方法

  • 將這些算法封裝成一個一個的類,任意的替換

  3. 優點

  • 算法可以自由切換
  • 避免使用多重條件判斷(如果不用策略模式我們可能會使用多重條件語句,不利於維護)
  • 擴展性良好,增加一個策略只需實現接口即可

  4. 缺點

  • 策略類數量會增多,每個策略都是一個類,復用的可能性很小
  • 所有的策略類都需要對外暴露

  5. 使用場景

  • 多個類只有算法或行為上稍有不同的場景
  • 算法需要自由切換的場景
  • 需要屏蔽算法規則的場景

  6. 應用實例

  • 出行方式,自行車、汽車等,每一種出行方式都是一個策略
  • 商場促銷方式,打折、滿減等
  • Java AWT中的LayoutManager,即布局管理器

  7. 注意事項

  • 如果一個系統的策略多於四個,就需要考慮使用混合模式來解決策略類膨脹的問題

三、策略模式的實現

  下面就以商場促銷為例使用策略模式實現商場促銷算法。UML圖如下:

  1. 上下文類

  首先聲明一個CashSuper對象,通過構造方法,傳入具體的收費策略,getResult()方法的功能為根據收費策略的不同獲得計算結果。

 1 public class CashContext {
 2     
 3     private CashSuper cashSuper;
 4     
 5     public CashContext(CashSuper cashSuper) {
 6         this.cashSuper = cashSuper;
 7     }
 8     
 9     public double getResult(double money) {
10         return cashSuper.acceptCash(money);
11     }
12 
13 }

  2. 現金收費抽象類

  策略類,為抽象類,抽象出收費的方法供子類實現。

1 public abstract class CashSuper {
2     
3     public abstract double acceptCash(double money);
4 
5 }

  3. 正常收費子類

  沒有任何活動的情況,正常收費,返回原價。

1 public class CashNormal extends CashSuper {
2 
3     @Override
4     public double acceptCash(double money) {
5         return money;
6     }
7 
8 }

  4. 打折收費子類

  打折活動,根據折扣返回打折后的價格。

 1 public class CashRebate extends CashSuper {
 2     
 3     private double moneyRebate = 1;    //折扣
 4     
 5     public CashRebate(double moneyRebate) {
 6         this.moneyRebate = moneyRebate;
 7     }
 8 
 9     @Override
10     public double acceptCash(double money) {
11         return money * moneyRebate;
12     }
13 
14 }

  5. 返利收費子類

  返利活動,輸入返利條件和返利值,比如滿300返100,moneyCoditation為300,moneyReturn為100。

   result = money - Math.floor(money / moneyConditation) * moneyReturn; 的意思為,如果當前金額大於等於返利條件,則使用當前金額減去返利值。

 1 public class CashReturn extends CashSuper {
 2 
 3     private double moneyConditation = 0.0;    //返利條件
 4     private double moneyReturn = 0.0d;    //返利值
 5     
 6     public CashReturn(double moneyConditation, double moneyReturn) {
 7         this.moneyConditation = moneyConditation;
 8         this.moneyReturn = moneyReturn;
 9     }
10 
11     @Override
12     public double acceptCash(double money) {
13         double result = money;
14         
15         if (money >= moneyConditation) {
16             result = money - Math.floor(money / moneyConditation) * moneyReturn;
17         }
18         
19         return result;
20     }
21 
22 }

  6. Client客戶端

  下面寫一個簡單的程序測試一下上方編寫的代碼。

 1 public class Client {
 2     
 3     public static void main(String[] args) {
 4         CashContext cashContext = null;
 5         
 6         Scanner scanner = new Scanner(System.in);
 7         System.out.print("請輸入打折方式(1/2/3):");
 8         int in = scanner.nextInt();
 9         String type = "";
10         
11         switch (in) {
12             case 1:
13                 cashContext = new CashContext(new CashNormal());
14                 type += "正常收費";
15                 break;
16                 
17             case 2:
18                 cashContext = new CashContext(new CashReturn(300, 100));
19                 type += "滿300返100";
20                 break;
21                 
22             case 3:
23                 cashContext = new CashContext(new CashRebate(0.8));
24                 type += "打8折";
25                 break;
26     
27             default:
28                 System.out.println("請輸入1/2/3");
29                 break;
30         }
31         
32         double totalPrices = 0;
33         
34         System.out.print("請輸入單價:");
35         double price = scanner.nextDouble();
36         System.out.print("請輸入數量:");
37         double num = scanner.nextDouble();
38         totalPrices = cashContext.getResult(price * num);
39         
40         System.out.println("單價:" + price + ",數量:" + num + ",類型:" + type + ",合計:" + totalPrices);
41         
42         scanner.close();
43     }
44 
45 }

  正常收費結果如下:

  

  返利收費結果如下:

  

  打折收費結果如下:

  

 

  源碼地址:https://gitee.com/adamjiangwh/GoF 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM