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.¿Patrón de estrategia sin declaraciones de "cambio"?
He leído que tener instrucciones 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 tiene ahora una declaración de cambio. Entiendo que podría crear fácilmente una nueva clase TriangleDrawer, y no tendría que abrir la clase Lápiz, 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 se muestra a continuación (Gracias a todos los que respondieron!) Esta solución me llegó al punto donde lo único que tengo que hacer para utilizar un nuevo idraw objeto es crearlo.
Estrategiapublic 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();
}
}
}
Cambiar la declaración que puede causar que el principio de apertura/cierre sea violado posteriormente es malo. El patrón de estrategia ayuda a separar el enunciado del conmutador del lugar donde desea mantenerlo cerrado, pero aún tiene que ocuparse de elegir la estrategia/implementación en algún lugar, ya sea en la declaración de conmutación, si/else/if, o utilizando LINQ Where (que es mi favorito :-) Por cierto, el patrón de estrategia también ayuda a las pruebas unitarias al permitirte simular fácilmente la implementación de la estrategia. – kimsk