1.1概述
方法是類中最重要的組成部分,一個方法的方法體由一系列語句構成,也就是說一個方法的方法體是一個算法。在某些設計中,一個類的設計人員經常可能涉及這樣的問題:由於用戶需求的變化,導致經常需要修改類中某個方法的方法體,即需要不斷地變化算法。在這樣的情況下可以考慮使用策略模式。
策略模式是處理算法不同變體的一種成熟模式,策略模式通過接口或抽象類封裝算法的標識,即在接口中定義一個抽象方法,實現該接口的類將實現接口中的抽象方法。策略模式把針對一個算法標識的一系列具體算法分別封裝在不同的類中,使得各個類給出的具體算法可以相互替換。
在策略模式中,封裝算法標識的接口稱作策略,實現該接口的類稱作具體策略。
1.2模式的結構
策略模式的結構包括三種角色:
(1)策略(Strategy):策略是一個接口,該接口定義若干個算法標識,即定義了若干個抽象方法。
(2)具體策略(ConcreteStrategy):具體策略是實現策略接口的類。具體策略實現策略接口所定義的抽象方法,即給出算法標識的具體算法。
(3)上下文(Context):上下文是依賴於策略接口的類,即上下文包含有策略聲明的變量。上下文中提供了一個方法,該方法委托策略變量調用具體策略所實現的策略接口中的方法。
策略模式接口的類圖如下所示:
1.3策略模式的優點
(1)上下文和具體策略是松耦合關系。因此上下文只知道它要使用某一個實現Strategy接口類的實例,但不需要知道具體是哪一個類。
(2)策略模式滿足“開-閉原則”。當增加新的具體策略時,不需要修改上下文類的代碼,上下文就可以引用新的具體策略的實例。
1.4適合使用策略模式的情景
(1)一個類定義了多種行為,並且這些行為在這個類的方法中以多個條件語句的形式出現,那么可以使用策略模式在類中使用大量的條件語句。
(2)程序不希望暴露復雜的、與算法有關的數據結構,那么可以使用策略模式來封裝算法。
(3)需要使用一個算法的不同變體。
1.5策略模式的使用
下面通過一個簡單的實例,實現1.1概述中簡單例子:在某種比賽中有若干個裁判,每位裁判給選手一個得分。選手的最后得分是根據全體裁判的得分計算出來的。請給出幾種計算選手得分的評分方案(策略),對於某次 比賽,可以從你的方案中選擇一種方案作為本次比賽的評分方案。
針對上述問題,使用策略模式設計若干個類。具體如下:
首先看一下本實例構建框架具體類和1.2模式的結構中類圖的對應關系,如下圖所示:
(1)策略(Strategy)
本問題中,策略接口的名字是ComputableStrategy,該接口規定的算法標識即抽象方法是double computeScore(double []a),ComputableStrategy接口的代碼如下:
package com.liuzhen.four_strategy; public interface ComputableStrategy { public abstract double computeScore(double[] a); }
(2)具體策略
對於本問題,有三個具體策略StrategyOne,StrategyTwo和StrategyTree。其中StrategyOne類實現為計算數組a的元素代數平均值;StrategyTwo類實現計算數組a的元素的幾何平均值;StrategyThree類實現為去掉數組a中最大元素和最小元素 ,然后計算剩余元素的代數平均值。
StrategyOne類代碼如下:
package com.liuzhen.four_strategy; public class StrategyOne implements ComputableStrategy{ public double computeScore(double[] a){ double score = 0 , sum = 0; for(int i = 0;i < a.length;i++){ sum += a[i]; } score = sum/a.length; return score; } }
StrategyTwo類代碼如下:
package com.liuzhen.four_strategy; public class StrategyTwo implements ComputableStrategy { public double computeScore(double[] a) { double score = 0 , multi = 1; int n = a.length; for(int i = 0;i < a.length;i++){ multi = multi*a[i]; } score = Math.pow(multi,1.0/n); return score; } }
StrategyThree類代碼如下:
package com.liuzhen.four_strategy; import java.util.Arrays; public class StrategyThree implements ComputableStrategy{ public double computeScore(double[] a){ if(a.length <= 2) return 0; double score = 0,sum = 0; Arrays.sort(a); for(int i = 1;i < a.length-1;i++){ sum += a[i]; } score = sum/(a.length-2); return score; } }
(3)上下文
上下文是GymnasticsGame類,該類包含策略聲明的變量,此變量可用於保存具體策略的引用。另外,GymnasticsGame類中包含一個double型數組a,a的元素代表各個裁判給選手的評分。該類中的getPersonScore(double a[])方法將委托具體策略的實例計算選手的最后得分。GymnasticsGame類的代碼如下:
package com.liuzhen.four_strategy; public class GymnasticsGame { ComputableStrategy strategy; public void setStrategy(ComputableStrategy strategy){ this.strategy = strategy; } public double getPersonScore(double[] a){ if(strategy != null) return strategy.computeScore(a); else return 0; } }
(4)具體使用
package com.liuzhen.four_strategy; public class FourApplication { public static void main(String args[]){ GymnasticsGame game = new GymnasticsGame(); //上下文對象 game.setStrategy(new StrategyOne()); //上下文對象使用策略一 Person zhang = new Person(); zhang.setName("張三"); double[] a = {9.12,9.25,8.87,9.99,6.99,7.88}; Person li = new Person(); li.setName("李四"); double[] b = {9.15,9.26,8.97,9.89,6.97,7.89}; zhang.setScore(game.getPersonScore(a)); li.setScore(game.getPersonScore(b)); System.out.printf("%s最后得分:%5.3f%n",zhang.getName(),zhang.getScore()); System.out.printf("%s最后得分:%5.3f%n",li.getName(),li.getScore()); game.setStrategy(new StrategyTwo()); //上下文對象使用策略二 zhang.setScore(game.getPersonScore(a)); li.setScore(game.getPersonScore(b)); System.out.println("使用幾何平均值方案:"); System.out.printf("%s最后得分:%5.3f%n",zhang.getName(),zhang.getScore()); System.out.printf("%s最后得分:%5.3f%n",li.getName(),li.getScore()); game.setStrategy(new StrategyThree()); //上下文對象使用策略三 zhang.setScore(game.getPersonScore(a)); li.setScore(game.getPersonScore(b)); System.out.println("使用(去掉最大值和最小值)算術平均值方案:"); System.out.printf("%s最后得分:%5.3f%n",zhang.getName(),zhang.getScore()); System.out.printf("%s最后得分:%5.3f%n",li.getName(),li.getScore()); } }
其中,Person類代碼如下:
package com.liuzhen.four_strategy; public class Person { String name; double score; public void setScore(double score){ this.score = score; } public void setName(String name){ this.name = name; } public double getScore(){ return score; } public String getName(){ return name; } }
運行結果如下:
張三最后得分:8.683 李四最后得分:8.688 使用幾何平均值方案: 張三最后得分:8.625 李四最后得分:8.631 使用(去掉最大值和最小值)算術平均值方案: 張三最后得分:8.780 李四最后得分:8.817
參考資料:
1.Java設計模式/耿祥義,張躍平著.——北京:清華大學出版社,2009.5