2014-12-01 53 views
3

我目前正在學習設計模式。在研究策略模式時,我發現了一些對我來說很陌生的事情。我尋找關於這種模式的討論,但沒有人回答我的問題......我如何實施戰略模式,讓它變得乾淨,保持封裝並使添加新戰略變得容易。在這裏解釋一下我的問題是,「規範」的戰略格局:Java策略模式 - 我可以在Context類中委託策略實例嗎?

public interface Strategy { 
    public void run(); 
} 

public class stratConcrt1 implements Strategy {/*run() implementation*/} 
public class stratConcrt2 implements Strategy {/*run() implementation*/} 

public class Context { 
    private Strategy strategy; 

    public Context(Strategy strat) { 
     this.strategy = strat; 
    } 

    public void runStrategy() { 
     this.strategy.run() 
    } 
} 


public class Client { 
    public void main(Strings[] args) { 
     Context cx; 

     cx = new Context(new stratConcrt1()) 
     cx.runStrategy(); 

     cx = new Context(new stratConcrt2()) 
     cx.runStrategy(); 
    } 
} 

我明白是怎麼回事,但我覺得奇怪,讓客戶知道一些事可以應用不同的策略。對我來說,讓上下文實例化不同的策略而不是客戶端會更清潔,因爲上下文涉及的策略應該(至少在我的腦海裏)是唯一能夠實例化策略的策略。

我實現了使用JavaFX與上面的代碼一定的差異一個小例子:

我有一個類字段實例座標列表,這個類對列表進行排序的方法。排序座標列表的方法是有幾種策略的方法。

public class Field { 

    // a field contains rectangles described in a list through their coordinates 
    private ObservableList<Coordinate> mpv_coordinateList = FXCollections 
        .observableArrayList(); 

    private Context mpv_sortContext; 

    // Constructor 
    public Field() { 
     //the rectangles are randomly created 
     Random rd = new Random(); 
     for (int i = 0; i < 100; i++) { 
      mpv_coordinateList.add(new Coordinate(rd.nextInt(490), rd.nextInt(490))); 
     } 

     //a context to dynamically modify the sort algorithm 
     mpv_sortContext = new Context(); 
    } 

    //returns the list with all rectangle 
    public ObservableList<Coordinate> getField() { 
     return this.mpv_coordinateList; 
    } 

    //sort elements (depending on different algorithms) 
    public ObservableList<Coordinate> sortElements(String p_sortToApply) { 
     return mpv_sortContext.launchSort(p_sortToApply, 
         this.mpv_coordinateList); 
    } 
} 

由於模型說,我創建了一個接口,讓從這個接口繼承了具體的策略:

public interface SortStrategy { 
    ObservableList<Coordinate> sort(ObservableList<Coordinate> p_listToSort); 
} 

public class EvenSort implements SortStrategy { 
    @Override 
    public ObservableList<Coordinate> sort(
       ObservableList<Coordinate> p_listToSort) { 

     ObservableList<Coordinate> oddCoordList = FXCollections 
         .observableArrayList(); 

     for (Coordinate coord : p_listToSort) { 
      if (coord.x % 2 == 0) { 
       oddCoordList.add(coord); 
      } 
     } 
     return oddCoordList; 
    } 
} 

public class OddSort implements SortStrategy { 
    @Override 
    public ObservableList<Coordinate> sort(
       ObservableList<Coordinate> p_listToSort) { 

     ObservableList<Coordinate> oddCoordList = FXCollections 
         .observableArrayList(); 

     for (Coordinate coord : p_listToSort) { 
      if (coord.x % 2 == 1) { 
       oddCoordList.add(coord); 
      } 
     } 
     return oddCoordList; 
    } 
} 

具體的類只返回一個包含所有具有偶數或奇數X座標列表座標。

,然後我創建了一個類背景:

public class Context { 
    //private SortStrategy mpv_sortStrategy; //never used 

    private EvenSort mpv_evenSort = new EvenSort(); 
    private OddSort mpv_oddSort = new OddSort(); 
    private StandardSort mpv_standardSort = new StandardSort(); 

    private HashMap<String, SortStrategy> mpv_HashMapStrategies; 


    public Context() { 

     //creation of a dictionary with all possible strategies 
     mpv_HashMapStrategies = new HashMap<String, SortStrategy>(); 
     mpv_HashMapStrategies.put("Even Sort", mpv_evenSort); 
     mpv_HashMapStrategies.put("Odd Sort", mpv_oddSort); 
     mpv_HashMapStrategies.put("Standard Sort", mpv_standardSort); 
    } 

    public ObservableList<Coordinate> launchSort(String p_sortToApply, ObservableList<Coordinate> p_listToSort){ 
     return mpv_HashMapStrategies.get(p_sortToApply).sort(p_listToSort); 
    } 
} 

通過GUI用戶可以選擇他想要使用排序列表中的策略。用戶可以點擊按鈕啓動排序。通過主類(未顯示)完成呼叫到inst_field.mpv_sortContext.sortElements(a_string),並使用字符串作爲描述要使用的策略的參數。然後在sortElements中使用此字符串來選擇用戶想要應用的策略的實例。

在我的實現中,我一方面是客戶端,另一方是處理策略(上下文,接口和具體類)的所有代碼。如果我想添加一個新策略,我只需要在Context類中添加一個新策略的實例,並在gui中描述這個新策略,以便讓用戶知道它。

我知道,在我做的實現也不是很好,因爲上下文包含每個可能的策略的實例,因爲我不需要對接口的引用,但我發現它比讓Field和客戶知道這些類。

嗯......我完全錯了嗎?我在「規範」戰略模式中錯過了什麼? 「規範」方式是實施戰略模式的唯一途徑嗎?還是有更好的方式來實現這種模式,只有那些應該知道的類能夠意識到策略實例,並且可以輕鬆地添加新策略?

回答

1

我看了有關這種模式的討論,但沒有回答我的問題......這是怎樣實現的策略模式,讓它乾淨

你的「戰略」並不一定是不潔,正如你所描述的那樣,我認爲你可能會因爲誰是客戶的想法而陷入困境。你的客戶正在提供使用的實現,但這可能是一個必要的實現細節。例如,java RMI tutorial's ComputeEngine基本上只使用這種模式。 「計算」實現由客戶端傳遞 - 因爲只有客戶端知道要執行的計算。

但是,更常見的是,該策略用於提供一種方法,使邏輯可以在某種情況下進行配置,或者允許針對特定用途量身定製公共上下文。它還具有根據需要隱藏客戶內部結構的好處。通常要做到這一點,使用的策略將在內部配置到上下文中。這可以通過提供:

  • 確定要被處理
  • 基於系統狀態或限制其確定策略的算法基於所述數據的策略算法
  • 一個配置文件使實施被加載(Class.getResourceAsStream)。這是Context班級地圖的擴展(即從知名地點加載地圖)。這裏的一個例子是,你可以提供一個「代表要使用的默認實現的策略,但允許提供新的實現作爲替代策略 - 例如,對於數據本身的控制器,例如defaultXMLParser
  • 。例如,對象類型可以規定:一定的策略來計算其價值。

對於上面的第一個兩分,你可以考慮使用一個工廠來獲得正確的策略,這將保持本地化實施的選擇問題。

好......我完全錯了嗎?我在「規範」戰略中錯過了什麼埃及模式。 「規範」方式是實施戰略模式的唯一途徑嗎?還是有更好的方式來實現這種模式,只有那些應該知道的類能夠意識到策略實例,並且可以輕鬆地添加新策略?

我會說你沒有錯。這實際上取決於戰略使用背後的目的。如果這是一個內部系統問題,那麼一些規則應該推動選擇(在工廠後面)。如果它是可配置的,那麼它應該由配置和隱藏在上下文中的管理(管理使用該策略的整體邏輯的類)驅動。但是,如果它依賴於用戶數據或行爲,那麼數據會在內部驅動選擇,或者您必須接受客戶將不得不通過您的策略。

還要注意,這種模式背後的目標是在保留替代實現的同時刪除條件邏輯。所以,如果你的策略導致你做了很多條件邏輯,那麼你可能需要重新考慮它是否澄清了你的代碼。

</warandpeace>

+0

我認爲你是對的,「我陷入了誰的客戶是誰的想法」......感謝真正有趣的鏈接 – 2014-12-08 07:04:06

2

你把所有的策略堆在一起 - 它不好。首先,戰略模式通常提供長期的功能,甚至是所有時間的應用程序都在運行。所以除了選擇之外,你不需要任何其他策略。所以,如果你有非常多的非常大的策略,你會堆積很多對象,你不需要。

另外不要忘記,你可以用不同的參數初始化你的策略,在你的情況下你有凍結的對象,你不能修改它們。

但是不要把每個模式看作是公理。你可以修改和使用它你想要什麼,你需要什麼。模式是主要模式,良好的做法等,但每個人都不能完美的解決所有問題。

0

你的第一個實施與模式的定義完全符合(按照經典的設計模式 - 可複用面向對象軟件的元素),恕我直言,你都歸因於它的目標,它只是從未有過的。您可能會丟失的關鍵點是Context包含(或能夠從環境中獲取)傳遞到封裝的ConcreteStrategy的數據,而無需客戶端的協作或瞭解。換句話說,客戶知道它希望應用什麼Strategy,但不知道Context中的數據。這是爲了簡單分離問題/解耦。

適應這些想法你的第一個例子,它可能已經閱讀:

public interface Strategy { 
    public void runOn(Context context); 
} 
public class ConcreteStrat1 implements Strategy { 
    public void runOn(Context context) { ... } 
} 
public class ConcreteStrat2 implements Strategy { 
    public void runOn(Context context) { ... } 
} 
public class Context { 
    private Strategy strategy; 
    private InformationPiece1 ip1; 
    private InformationPiece2 ip2; 
    private InformationPiece3 ip3; 
    ... 
    // These are the "ContextInterface()" methods: ways for the Strategy's and other clients to interact with the Context 
    public InformationPiece1 getIP1() { return this.ip1 ; } 
    public void setIP1(InformationPiece1 ip1) { this.ip1= ip1; } 
    public InformationPiece2 getIP2() { return this.ip2 ; } 
    public void setIP2(InformationPiece2 ip2) { this.ip2= ip2; } 
    public InformationPiece3 getIP3() { return this.ip3 ; } 
    public void setIP3(InformationPiece3 ip3) { this.ip3= ip3; } 
    ... 
    public Context(Strategy strategy){ 
     this.strategy= strategy ; 
    } 
    // This operation can be carried out according to a configurable Strategy 
    public void doSomething() { 
     this.strategy.runOn(this); 
    } 
    // This other doesn't. Or maybe it does, but with a second category of configurable Strategy's 
    public void doAnotherThing() { 
     ... 
    } 
} 
public class Client { 
    public void main(Strings[] args) { 
     Context cx; 
     // Decide with what Strategy to "configure" cx. 
     if(args[0].equalsIgnoreCase("A")) 
      cx= new Context(new ConcreteStrat2()); 
     else 
      cx= new Context(new ConcreteStrat1()); 
     // Populate cx. 
     new CIBuilder(cx).buildFrom("Maybe a file name? User interaction anyone?") ; 
     // Pass cx to another client, which would eventually call cx.doSomething(). 
     // This client doesn't need to know what Strategy will be called in turn by cx.doSomething(). 
     // In fact, it doesn't need to know that cx.doSomething() is implemented using the Strategy Pattern at all! 
     new DoesntKnowAboutStrategiesNorNeedsTo().process(cx) ; 
    } 
} 

通過這種方式,我們已經清楚地分隔的角色和responsabilities:

  • main()基於用戶輸入選擇Strategy (或者也許以後基於一個屬性文件;它會很容易改變)。它知道不同的可用Strategy的和Context接受建設,但沒有其他很多關於。從這個角度來看,main()Strategy模式的客戶端,但本身並沒有太多Context
  • Context是一個經典的類能夠存儲信息和執行操作。但是,這些操作之一可以通過幾種不同的方式執行(Strategy)。除此之外,Context不知道存在多少種不同的策略或它們如何運作。實際上,如果出現新的Strategy,則根本不需要修改Context(儘管它的接口的原始設計是好的)。
  • DoesntKnowAboutStrategiesNorNeedsToStrategy完全隔離,以及選擇一個的過程。它只知道Context的接口,而不是它們的實現。即使doSomething()最初在沒有Strategy模式的情況下實施,DoesntKnowAboutStrategiesNorNeedsTo在更改後也不需要修改。這是Context的主要客戶,但根本不是Strategy模式。
0

它看起來對我說,你正在尋找其中主要關注的是相對於具有能夠與一個配置對象Strategy選擇的設計模式。如果這是這種情況,則最適合的模式是Chain of Responsibility。它與你的第二個例子類似。然而,考慮到每個Handler(與Strategy相同的模式)都有機會自行決定它是否可以應用於數據,所以它更加模塊化和可擴展。現在

,如果目標是決定最好Strategy(相對於可在給定的數據操作第一),我都用過,一個相對簡單的變型(和我在我之前肯定很多人一樣)是定義一個成本標準。執行時間,要使用的最大內存或兩者的組合都是很好的例子。每個Handler/Strategy需要知道如何快速估算給定數據的成本。定義該基本概念後,有兩個主要的實現方案:

  1. 定義類似於Context在第二示例中利用目錄的Handler/Strategy秒的中央控制器。可能預定義或可能以某種方式配置。該控制器每Handler評估數據和產生成本估計,並調用最小的做真正的處理。這實際上與Chain of Responsability的精神有很大差異,因此認爲它是一個變體,但是由於它與原始代碼的相似性,所以想指出它。另一個變體是使控制器估計每個Handler的成本,但是在這種情況下,問題以錯誤的方式分離,並且組件之間的耦合結果巨大。

  2. Handler S IN沒有找到能夠處理Request(數據)的第一個合作,但是根據成本估算找到最好

下面是替代2的一般代碼,其中介紹的Chain of Responsibility多種利益(以及其相對較少的缺點)。

public abstract class Handler { 
    private Handler successor ; 
    public void setSuccessor(Handler successor) { this.successor= successor ; } 
    public abstract double estimateCostFor(Information info) ; 
    public abstract void doProcess(Information info) ; 
    public boolean process(Information info) { 
     return this.processIfBetterThan(Double.MAX_VALUE,info); 
    } 
    public boolean processIfBetterThan(double callersCost,Information info) { 
     double myCost= this.estimateCostFor(info) ; 
     double minCostSoFar= Math.min(callersCost,myCost) ; 
     boolean informationProcessed= false ; 
     if(this.successor != null) 
      informationProcessed= this.successor.processIfBetterThan(minCostSoFar,info) ; 
     if(! informationProcessed && myCost <= minCostSoFar) { 
     // In cases like this, I prefer <= to == especially when dealing with floating point variables. 
     // Much safer! 
      this.doProcess(info); 
      informationProcessed= true ; 
     } 
     return informationProcessed ; 
    } 
} 
public class ConcreteHandler1 implements Handler { 
    public double estimateCostFor(Information info) { ... } 
    public void doProcess(Information info) { ... } 
} 
public class ConcreteHandler2 implements Handler { 
    public double estimateCostFor(Information info) { ... } 
    public void doProcess(Information info) { ... } 
} 
public class ConcreteHandler3 implements Handler { 
    public double estimateCostFor(Information info) { ... } 
    public void doProcess(Information info) { ... } 
} 
public class Client { 
    public void main(Strings[] args) { 
     // Setup chain of responsibility 
     Handler startOfChain= new ConcreteHandler1() ; 
     Handler h2= new ConcreteHandler2() ; 
     startOfChain.setSuccessor(h2); 
     Handler h3= new ConcreteHandler3() ; 
     h2.setSuccessor(h3); 
     // Obtain Information to process 
     Information myInfo= ... ; 
     // Process it with the best Handler/Strategy 
     startOfChain.process(info); 
    } 
}