没有“开关”语句的策略模式?

我一直在阅读战略模式,并有一个问题。 我已经在下面实现了一个非常基本的控制台应用程序来解释我在问什么。

我已经读到,在实施战略模式时,“切换”语句是一个红旗。 然而,在这个例子中,我似乎无法摆脱开关语句的束缚。 我错过了什么吗? 我能够从铅笔删除逻辑,但我的现在有一个switch语句。 我明白,我可以轻松地创build一个新的TriangleDrawer类,而不必打开铅笔类,这是很好的。 但是,我需要打开Main,以便知道将哪种types的IDrawer传递给铅笔 。 这只是如果我依靠用户input需要做什么? 如果没有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(); } } } 

实施的解决scheme如下所示 (感谢所有谁回应!)这个解决scheme让我到了我需要做的唯一事情就是使用一个新的IDraw对象来创build它。

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

策略不是一个神奇的反开关解决scheme。 它所做的是给你的代码模块化,所以,而不是一个大的交换机和业务逻辑都混在了维修的噩梦

  • 您的业​​务逻辑是孤立的,可以扩展
  • 您可以select如何创build具体的类(请参阅工厂模式)
  • 您的基础设施代码(您的主要)可以非常干净,没有两者

例如 – 如果你在你的main方法中使用了switch,并创build了一个接受命令行参数的类并返回了一个IDraw的实例(即它封装了这个开关),那么你的main就再次被清理了,是要实现这个select。

以下是针对您的问题的过度devise的解决scheme,仅仅是为了避免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"); } 

我不认为你的演示应用程序中的开关实际上是战略模式本身的一部分,它只是被用来执行你定义的两种不同的策略。

“交换机是红旗”警告是指在策略内部有交换机; 例如,如果您定义了一个策略“GenericDrawer”,并确定用户是否想要一个SquareDrawer或CircleDrawer在内部使用一个参数值的开关,那么您将不会获得策略模式的好处。

你也可以在字典的帮助下摆脱

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