没有“开关”的策略模式声明?
我一直在阅读有关策略模式的内容,并且有一个问题。我在下面实现了一个非常基本的控制台应用程序来解释我的要求。
我读到,在实现策略模式时,使用“switch”语句是一个危险信号。然而,我似乎无法摆脱这个例子中的 switch 语句。我错过了什么吗?我能够从 Pencil 中删除逻辑,但我的 Main 现在有一个 switch 语句。我知道我可以轻松创建一个新的 TriangleDrawer 类,而不必打开 Pencil 类,这很好。但是,我需要打开Main,以便它知道将哪种类型的IDrawer传递给Pencil。如果我依赖用户输入,这就是需要做的事情吗?如果有一种方法可以在不使用 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();
}
}
}
I've been doing some reading on the Strategy Pattern, and have a question. I have implemented a very basic Console Application below to explain what I'm asking.
I have read that having 'switch' statements is a red flag when implementing the strategy pattern. However, I can't seem to get away from having a switch statement in this example. Am I missing something? I was able to remove the logic from the Pencil, but my Main has a switch statement in it now. I understand that I could easily create a new TriangleDrawer class, and wouldn't have to open the Pencil class, which is good. However, I would need to open Main so that it would know which type of IDrawer to pass to the Pencil. Is this just what needs to be done if I'm relying on the user for input? If there's a way to do this without the switch statement, I'd love to see it!
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();
}
}
}
Implemented Solution shown below (Thanks to all who responded!)
This solution got me to the point where the only thing I need to do to use a new IDraw object is to create it.
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();
}
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
策略并不是一种神奇的反转换解决方案。它所做的就是模块化您的代码,这样
例如 - 如果你在你的main方法中进行了切换并创建了一个接受命令行参数并返回IDraw实例的类(即它封装了那个开关)你的主干又干净了,你的开关属于一个唯一目的是实现这个选择的类。
Strategy isn't a magic anti-switch solution. What it does do is give modularise your code so that instead of a big switch and business logic all mixed up in a maintenance nightmare
For example - if you took the switch in your main method and created a class which accepted the command line argument and returned an instance of IDraw (i.e. it encapsulates that switch) your main is clean again and your switch is in a class whose sole purpose is to implement that choice.
以下是针对您的问题的过度设计的解决方案,仅仅是为了避免
if
/switch
语句。The following is an over engineered solution to your problem solely for the sake of avoiding
if
/switch
statements.我不认为您的演示应用程序中的开关实际上是策略模式本身的一部分,它只是用于练习您定义的两种不同策略。
“开关是危险信号”警告是指策略内有开关;例如,如果您定义了一个策略“GenericDrawer”,并让它在内部使用针对参数值的开关来确定用户是否想要 SquareDrawer 或 CircleDrawer,那么您将无法从该策略模式中受益。
I don't think your switch here in your demo app is actually part of the strategy pattern itself, it is just being used to exercise the two different strategies you have defined.
The "switches being a red flag" warning refers to having switches inside the strategy; for example, if you defined a strategy "GenericDrawer", and had it determine if the user wanted a SquareDrawer or CircleDrawer internally using a switch against a parameter value, you would not be getting the benefit of the strategy pattern.
您还可以借助字典摆脱
if
You can also get rid of
if
with help of a dictionary有点晚了,但对于任何仍然有兴趣完全删除条件语句的人来说。
A little to late but for anyone that still is interested in fully removing a conditional statement.