sirve qué para nacionales llenar llenado iva iue instalador impuestos imprimir formularios formulario facilito descargar c# design-patterns dependency-injection strategy-pattern coding-style

c# - qué - Patrón de estrategia sin declaraciones de ''cambio''?



llenado de formularios iva it iue rc-iva (5)

La estrategia no es una solución mágica anti-switch. Lo que sí hace es modularizar su código para que en lugar de un gran interruptor y lógica de negocios se mezclen en una pesadilla de mantenimiento

  • su lógica de negocio está aislada y abierta para la extensión
  • tienes opciones en cuanto a cómo creas tus clases concretas (ver patrones de fábrica, por ejemplo)
  • su código de infraestructura (su principal) puede ser muy limpio, libre de ambos

Por ejemplo, si tomó el interruptor en su método principal y creó una clase que aceptó el argumento de línea de comando y devolvió una instancia de IDraw (es decir, encapsula ese interruptor), su principal está limpia nuevamente y su interruptor está en una clase cuyo único propósito es implementar esa elección.

He estado leyendo el Patrón de estrategia y tengo una pregunta. He implementado una aplicación de consola muy básica a continuación para explicar lo que estoy preguntando.

He leído que tener declaraciones de ''cambio'' es una señal de alerta cuando se implementa el patrón de estrategia. Sin embargo, parece que no puedo evitar tener una declaración de cambio en este ejemplo. ¿Me estoy perdiendo de algo? Pude eliminar la lógica del Lápiz , pero mi Principal ahora tiene una declaración de cambio. Entiendo que podría crear fácilmente una nueva clase TriangleDrawer , y no tendría que abrir la clase Pencil , lo cual es bueno. Sin embargo, necesitaría abrir Main para saber qué tipo de IDrawer pasar al Lápiz . ¿Es esto exactamente lo que se necesita hacer si confío en el usuario para la entrada? Si hay una manera de hacer esto sin la declaración de cambio, ¡me encantaría verlo!

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

Solución implementada que se muestra a continuación (¡Gracias a todos los que respondieron!) Esta solución me llevó al punto en que lo único que tengo que hacer para usar un nuevo objeto IDraw es crearlo.

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


La siguiente es una solución sobre ingeniería para su problema únicamente con el fin de evitar declaraciones 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"); }


No creo que su cambio aquí en su aplicación de demostración sea en realidad parte del patrón de estrategia en sí, solo se está utilizando para ejercitar las dos estrategias diferentes que ha definido.

La advertencia "los interruptores son un indicador rojo" se refiere a tener interruptores dentro de la estrategia; por ejemplo, si definió una estrategia "GenericDrawer" y lo hizo determinar si el usuario quería un SquareDrawer o un CircleDrawer internamente usando un interruptor contra un valor de parámetro, no obtendría el beneficio del patrón de estrategia.


También puedes deshacerte if con la ayuda de un diccionario

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


Un poco tarde pero para cualquier persona que todavía esté interesada en eliminar por completo un enunciado condicional.

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