43

我一直在做一些閱讀戰略模式,並有一個問題。我已經在下面實現了一個非常基本的控制檯應用程序來解釋我在問什麼。沒有'switch'語句的策略模式?

我已經讀過,在執行策略模式時,'switch'語句是紅旗。不過,在這個例子中,我似乎無法擺脫switch語句。我錯過了什麼嗎?我能夠從鉛筆刪除邏輯,但我的現在有一個switch語句。我明白我可以輕鬆創建一個新的TriangleDrawer類,而不必打開鉛筆類,這很好。但是,我需要打開Main以便它知道哪種類型的IDrawer要傳遞給鉛筆。如果我依靠用戶進行輸入,這只是需要做什麼?如果沒有switch語句的話,我很樂意看到它!下面所示

class Program 
{ 
    public class Pencil 
    { 
     private IDraw drawer; 

     public Pencil(IDraw iDrawer) 
     { 
      drawer = iDrawer; 
     } 

     public void Draw() 
     { 
      drawer.Draw(); 
     } 
    } 

    public interface IDraw 
    { 
     void Draw(); 
    } 

    public class CircleDrawer : IDraw 
    { 
     public void Draw() 
     { 
      Console.Write("()\n"); 
     } 
    } 

    public class SquareDrawer : IDraw 
    { 
     public void Draw() 
     { 
      Console.WriteLine("[]\n"); 
     } 
    } 

    static void Main(string[] args) 
    { 
     Console.WriteLine("What would you like to draw? 1:Circle or 2:Sqaure"); 

     int input; 
     if (int.TryParse(Console.ReadLine(), out input)) 
     { 
      Pencil pencil = null; 

      switch (input) 
      { 
       case 1: 
        pencil = new Pencil(new CircleDrawer()); 
        break; 
       case 2: 
        pencil = new Pencil(new SquareDrawer()); 
        break; 
       default: 
        return; 
      } 

      pencil.Draw(); 

      Console.WriteLine("Press any key to exit..."); 
      Console.ReadKey(); 
     } 
    } 
} 

實施的解決方案(感謝所有誰回答!) 該解決方案讓我的地步,我需要做的唯一的事情用一個新的IDraw目的是創造它。

public class Pencil 
    { 
     private IDraw drawer; 

     public Pencil(IDraw iDrawer) 
     { 
      drawer = iDrawer; 
     } 

     public void Draw() 
     { 
      drawer.Draw(); 
     } 
    } 

    public interface IDraw 
    { 
     int ID { get; } 
     void Draw(); 
    } 

    public class CircleDrawer : IDraw 
    { 

     public void Draw() 
     { 
      Console.Write("()\n"); 
     } 

     public int ID 
     { 
      get { return 1; } 
     } 
    } 

    public class SquareDrawer : IDraw 
    { 
     public void Draw() 
     { 
      Console.WriteLine("[]\n"); 
     } 

     public int ID 
     { 
      get { return 2; } 
     } 
    } 

    public static class DrawingBuilderFactor 
    { 
     private static List<IDraw> drawers = new List<IDraw>(); 

     public static IDraw GetDrawer(int drawerId) 
     { 
      if (drawers.Count == 0) 
      { 
       drawers = Assembly.GetExecutingAssembly() 
            .GetTypes() 
            .Where(type => typeof(IDraw).IsAssignableFrom(type) && type.IsClass) 
            .Select(type => Activator.CreateInstance(type)) 
            .Cast<IDraw>() 
            .ToList(); 
      } 

      return drawers.Where(drawer => drawer.ID == drawerId).FirstOrDefault(); 
     } 
    } 

    static void Main(string[] args) 
    { 
     int input = 1; 

     while (input != 0) 
     { 
      Console.WriteLine("What would you like to draw? 1:Circle or 2:Sqaure"); 

      if (int.TryParse(Console.ReadLine(), out input)) 
      { 
       Pencil pencil = null; 

       IDraw drawer = DrawingBuilderFactor.GetDrawer(input); 

       pencil = new Pencil(drawer); 
       pencil.Draw(); 
      } 
     } 
    } 
+1

可能導致開/關原理被違反的開關語句是壞的。策略模式有助於將switch語句從希望保持它關閉的地方分離出來,但是仍然需要處理選擇策略/實現的地方,無論是switch語句,if/else/if還是使用LINQ Where(這是我的最愛:-)順便說一下,策略模式也可以幫助您輕鬆地模擬策略實施,從而幫助單元測試。 – kimsk 2013-01-23 06:35:12

回答

46

策略不是一種神奇的反開關解決方案。它所做的是給modularise你的代碼,這樣,而不是一個大的交換機和業務邏輯都在維護的噩夢混了

  • 你的業務邏輯是孤立的,開放的擴展
  • 你有選項如何您創建的具體類(見例如工廠模式)
  • 您的基礎架構的代碼(你的主)可以很乾淨,免費的都

例如 - 如果你在你的主要方法,把開關和創建一個接受命令行argume的類nt並返回IDraw的實例(即,它封裝了那個交換機)你的主機再次被清理乾淨,你的交換機在一個類中,其唯一目的是實現這個選擇。

+11

+1 - 我總是覺得戰略和工廠齊頭並進。 – 2010-09-30 19:44:29

+0

感謝您幫助我瞭解戰略模式是/不是。我編輯了我的帖子,以顯示我是如何實現這一目標的。 – JSprang 2010-10-01 20:46:34

+0

@Brabster有時我覺得很難趕上適當的時刻,當你應該放棄開關語句和切換(雙關語意圖)到戰術模式。例如,如果你有30個非常小且簡單的開關情況,那麼在策略模式下會變成額外的30個類 - 是否需要切換或者更好地保留那些不太乾淨的代碼片段? – mmmm 2016-11-21 19:33:52

13

我不認爲您在演示應用程序中的切換實際上是戰略模式本身的一部分,它只是用於鍛鍊您定義的兩種不同策略。

「交換機爲紅旗」警告是指在內部有交換機的策略;例如,如果您定義了一個策略「GenericDrawer」,並確定用戶是否想要一個SquareDrawer或CircleDrawer內部使用參數值的開關,則您不會獲得策略模式的好處。

15

以下是針對您的問題的過度設計解決方案,目的僅僅是爲了避免if/switch陳述。

CircleFactory: IDrawFactory 
{ 
    string Key { get; } 
    IDraw Create(); 
} 

TriangleFactory: IDrawFactory 
{ 
    string Key { get; } 
    IDraw Create(); 
} 

DrawFactory 
{ 
    List<IDrawFactory> Factories { get; } 
    IDraw Create(string key) 
    { 
     var factory = Factories.FirstOrDefault(f=>f.Key.Equals(key)); 
     if (factory == null) 
      throw new ArgumentException(); 
     return factory.Create(); 
    } 
} 

void Main() 
{ 
    DrawFactory factory = new DrawFactory(); 
    factory.Create("circle"); 
} 
11

還可以獲得與字典

Dictionary<string, Func<IDraw> factory> drawFactories = new Dictionary<string, Func<IDraw> factory>() { {"circle", f=> new CircleDraw()}, {"square", f=> new SquareDraw()}}(); 

Func<IDraw> factory; 
drawFactories.TryGetValue("circle", out factory); 

IDraw draw = factory(); 
+0

我喜歡這個!我擴展它來從我的依賴注入容器填充工廠字典,註冊工廠接口的多個命名實現。 – Will 2017-04-03 17:58:52

0

有點晚,但對任何人仍然有興趣完全去除條件語句的幫助下襬脫if

 class Program 
    { 
     Lazy<Dictionary<Enum, Func<IStrategy>>> dictionary = new Lazy<Dictionary<Enum, Func<IStrategy>>>(
      () => 
       new Dictionary<Enum, Func<IStrategy>>() 
       { 
        { Enum.StrategyA, () => { return new StrategyA(); } }, 
        { Enum.StrategyB, () => { return new StrategyB(); } } 
       } 
      ); 

     IStrategy _strategy; 

     IStrategy Client(Enum enu) 
     { 
      Func<IStrategy> _func 
      if (dictionary.Value.TryGetValue(enu, out _func)) 
      { 
       _strategy = _func.Invoke(); 
      } 

      return _strategy ?? default(IStrategy); 
     } 

     static void Main(string[] args) 
     { 
      Program p = new Program(); 

      var x = p.Client(Enum.StrategyB); 
      x.Create(); 
     } 
    } 

    public enum Enum : int 
    { 
     StrategyA = 1, 
     StrategyB = 2 
    } 

    public interface IStrategy 
    { 
     void Create(); 
    } 
    public class StrategyA : IStrategy 
    { 
     public void Create() 
     { 
      Console.WriteLine("A"); 
     } 
    } 
    public class StrategyB : IStrategy 
    { 
     public void Create() 
     { 
      Console.WriteLine("B"); 
     } 
    }