52

私は戦略パターンについて読んでいて、質問があります。私が求めていることを説明するために、以下の非常に基本的なコンソール アプリケーションを実装しました。

戦略パターンを実装する際に「switch」ステートメントを使用することは危険信号であると読みました。ただし、この例では switch ステートメントを使用することから逃れられないようです。何か不足していますか?Pencilからロジックを削除することができましたが、Mainには switch ステートメントが含まれています。新しいTriangleDrawerクラスを簡単に作成でき、 Pencilクラスを開く必要がないことは理解しています。これは良いことです。ただし、鉛筆に渡すIDrawerのタイプを知るためにMainを開く必要があります。. ユーザーの入力に頼っている場合、これは必要なことですか? 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();
            }
        }
    }
4

5 に答える 5

61

戦略は魔法のアンチスイッチ ソリューションではありません。それが行うことは、メンテナンスの悪夢の中で大きなスイッチとビジネス ロジックがすべて混同される代わりに、コードをモジュール化することです。

  • ビジネス ロジックは分離されており、拡張可能です。
  • 具体的なクラスを作成する方法に関するオプションがあります(たとえば、ファクトリパターンを参照してください)
  • インフラストラクチャ コード (メイン) は非常にクリーンで、どちらもありません。

たとえば、メイン メソッドでスイッチを取得し、コマンド ライン引数を受け入れて IDraw のインスタンスを返すクラスを作成した場合 (つまり、そのスイッチをカプセル化した場合)、メインは再びクリーンになり、スイッチは唯一の目的を持つクラスに含まれます。その選択を実行することです。

于 2010-09-30T19:31:20.010 に答える
16

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");
}
于 2010-09-30T19:45:16.907 に答える
15

ここでのデモ アプリのスイッチは、実際には戦略パターン自体の一部ではないと思います。定義した 2 つの異なる戦略を実行するために使用されているだけです。

「スイッチは危険信号です」という警告は、戦略内にスイッチがあることを示しています。たとえば、戦略「GenericDrawer」を定義し、パラメーター値に対するスイッチを内部的に使用して、ユーザーが SquareDrawer または CircleDrawer を必要としているかどうかを判断した場合、戦略パターンの利点は得られません。

于 2010-09-30T19:34:56.853 に答える
14

if辞書の助けを借りて取り除くこともできます

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();
于 2010-09-30T19:53:34.520 に答える