Wzór strategii bez deklaracji "switch"?

Poczytałem trochę o schemacie strategii i mam pytanie. Zaimplementowałem poniżej bardzo podstawową aplikację konsolową, aby wyjaśnić, o co Proszę.

Czytałem, że posiadanie instrukcji "switch" jest czerwoną flagą podczas wdrażania wzorca strategii. Jednak w tym przykładzie nie mogę uciec od instrukcji switch. Coś przeoczyłem? Udało mi się usunąć logikę z ołówka , ale mój Główny ma przełącznik oświadczenie w nim teraz. Rozumiem, że mógłbym łatwo utworzyć nową klasę TriangleDrawer i nie musiałbym otwierać klasy Pencil , co jest dobre. Jednak musiałbym otworzyć Main, aby wiedział, który typ IDrawerprzekazać do Pencil. Czy to jest właśnie to, co należy zrobić, jeśli polegam na użytkowniku za wejście? Jeśli jest sposób, aby to zrobić bez instrukcji switch, chciałbym to zobaczyć!

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();
        }
    }
}

Wdrożone Rozwiązanie pokazane poniżej (dziękujemy wszystkim, którzy odpowiedzieli!) To rozwiązanie doprowadziło mnie do tego, że jedyne, co muszę zrobić, aby użyć nowego obiektu IDraw , to go utworzyć.

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();
            }
        }
    }
Author: JSprang, 2010-09-30

5 answers

Strategia nie jest magicznym rozwiązaniem anty-switch. To co robi to daje modularyzację kodu tak, że zamiast dużego przełącznika i logiki biznesowej wszystko pomieszane w koszmarze konserwacji

  • twoja logika biznesowa jest odizolowana i otwarta dla rozszerzenia
  • masz opcje, jak tworzyć konkretne klasy (patrz przykład wzorce fabryczne)
  • twój kod infrastruktury (twój główny) może być bardzo czysty, wolny od obu

Na przykład - jeśli wziąłeś przełącznik w metodzie main i utworzyła klasę, która zaakceptowała argument wiersza poleceń i zwróciła instancję IDraw (tzn. hermetyzuje ten switch) Twój main jest ponownie czysty, a Twój switch jest w klasie, której jedynym celem jest zaimplementowanie tego wyboru.

 59
Author: brabster,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-05-17 19:04:35

Poniżej znajduje się zbyt zaprojektowane rozwiązanie twojego problemu wyłącznie w celu uniknięcia if/switch Oświadczenia.

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");
}
 17
Author: Muhammad Hasan Khan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-12-08 15:32:52

Nie sądzę, aby twój przełącznik tutaj w aplikacji demo jest rzeczywiście częścią samego wzorca strategii, jest po prostu używany do wykonywania dwóch różnych strategii, które zdefiniowałeś.

Na przykład, jeśli zdefiniujesz strategię "GenericDrawer" i określisz, czy użytkownik chce użyć SquareDrawer lub CircleDrawer wewnętrznie używając przełącznika w stosunku do wartości parametru, nie uzyskasz dostępu do tej strategii. korzyści płynące ze wzoru strategii.
 15
Author: Guy Starbuck,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-09-30 19:34:56

Możesz również pozbyć się if za pomocą słownika

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();
 15
Author: Muhammad Hasan Khan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-12-08 15:33:22

Trochę za późno, ale dla każdego, kto nadal jest zainteresowany całkowitym usunięciem instrukcji warunkowej.

     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");
        }
    }
 5
Author: boris,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-01-29 15:34:18