C# 大型 Switch 语句:糟糕的 OOP?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/505454/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Large Switch statements: Bad OOP?
提问by Erik Funkenbusch
I've always been of the opinion that large switch statements are a symptom of bad OOP design. In the past, I've read articles that discuss this topic and they have provided altnerative OOP based approaches, typically based on polymorphism to instantiate the right object to handle the case.
我一直认为大型 switch 语句是糟糕的 OOP 设计的症状。过去,我读过讨论这个主题的文章,他们提供了替代的基于 OOP 的方法,通常基于多态来实例化正确的对象来处理这种情况。
I'm now in a situation that has a monsterous switch statement based on a stream of data from a TCP socket in which the protocol consists of basically newline terminated command, followed by lines of data, followed by an end marker. The command can be one of 100 different commands, so I'd like to find a way to reduce this monster switch statement to something more manageable.
我现在处于一种基于来自 TCP 套接字的数据流的怪物 switch 语句的情况,其中协议基本上由换行符终止命令组成,后跟数据行,后跟结束标记。该命令可以是 100 个不同命令中的一个,所以我想找到一种方法来减少这个怪物 switch 语句,使其更易于管理。
I've done some googling to find the solutions I recall, but sadly, Google has become a wasteland of irrelevant results for many kinds of queries these days.
我已经做了一些谷歌搜索来找到我记得的解决方案,但遗憾的是,如今谷歌已经成为许多类型查询的无关结果的荒地。
Are there any patterns for this sort of problem? Any suggestions on possible implementations?
这类问题有什么模式吗?关于可能的实现有什么建议吗?
One thought I had was to use a dictionary lookup, matching the command text to the object type to instantiate. This has the nice advantage of merely creating a new object and inserting a new command/type in the table for any new commands.
我的一个想法是使用字典查找,将命令文本与要实例化的对象类型相匹配。这有一个很好的优点,即仅创建一个新对象并在表中为任何新命令插入一个新命令/类型。
However, this also has the problem of type explosion. I now need 100 new classes, plus I have to find a way to interface them cleanly to the data model. Is the "one true switch statement" really the way to go?
但是,这也存在类型爆炸的问题。我现在需要 100 个新类,而且我必须找到一种方法将它们干净地连接到数据模型。“一个真正的 switch 语句”真的是要走的路吗?
I'd appreciate your thoughts, opinions, or comments.
我很感激你的想法、意见或评论。
采纳答案by nezroy
You may get some benefit out of a Command Pattern.
您可能会从命令模式中获得一些好处。
For OOP, you may be able to collapse several similar commands each into a single class, if the behavior variations are small enough, to avoid a complete class explosion (yeah, I can hear the OOP gurus shrieking about that already). However, if the system is already OOP, and each of the 100+ commands is truly unique, then just make them unique classes and take advantage of inheritance to consolidate the common stuff.
对于 OOP,如果行为变化足够小,您可以将几个类似的命令分别折叠到一个类中,以避免类完全爆炸(是的,我已经听到 OOP 大师们在尖叫了)。但是,如果系统已经是面向对象的,并且 100 多个命令中的每一个都确实是独一无二的,那么只需让它们成为唯一的类并利用继承来整合常见的东西。
If the system is not OOP, then I wouldn't add OOP just for this... you can easily use the Command Pattern with a simple dictionary lookup and function pointers, or even dynamically generated function calls based on the command name, depending on the language. Then you can just group logically associated functions into libraries that represent a collection of similar commands to achieve manageable separation. I don't know if there's a good term for this kind of implementation... I always think of it as a "dispatcher" style, based on the MVC-approach to handling URLs.
如果系统不是 OOP,那么我不会为此添加 OOP……您可以通过简单的字典查找和函数指针轻松使用命令模式,甚至可以根据命令名称动态生成函数调用,具体取决于语言。然后,您可以将逻辑关联的函数分组到代表一组相似命令的库中,以实现可管理的分离。我不知道这种实现是否有一个好的术语……我一直认为它是一种“调度程序”风格,基于处理 URL 的 MVC 方法。
回答by Otávio Décio
One way I see you could improve that would make your code driven by the data, so for example for each code you match something that handles it (function, object). You could also use reflection to map strings representing the objects/functions and resolve them at run time, but you may want to make some experiments to assess performance.
我认为您可以改进的一种方法是使您的代码由数据驱动,例如,对于每个代码,您都匹配处理它的某些内容(函数、对象)。您还可以使用反射来映射表示对象/函数的字符串并在运行时解析它们,但您可能需要进行一些实验来评估性能。
回答by Jason Punyon
I see the strategy pattern. If I have 100 different strategies...so be it. The giant switch statement is ugly. Are all the Commands valid classnames? If so, just use the command names as class names and create the strategy object with Activator.CreateInstance.
我看到了策略模式。如果我有 100 种不同的策略……就这样吧。巨大的 switch 语句很丑陋。所有命令都是有效的类名吗?如果是这样,只需使用命令名称作为类名称并使用 Activator.CreateInstance 创建策略对象。
回答by Loren Pechtel
I think this is one of the few cases where large switches are the best answer unless some other solution presents itself.
我认为这是少数情况下大型交换机是最佳答案的情况之一,除非出现其他解决方案。
回答by ChrisW
I see having twoswitch statements as a symptom of non-OO design, where the switch-on-enum-type might be replaced with multiple types which provide different implementations of an abstract interface; for example, the following ...
我认为有两个switch 语句是非 OO 设计的一种症状,其中 switch-on-enum-type 可能会被多种类型替换,这些类型提供抽象接口的不同实现;例如,以下...
switch (eFoo)
{
case Foo.This:
eatThis();
break;
case Foo.That:
eatThat();
break;
}
switch (eFoo)
{
case Foo.This:
drinkThis();
break;
case Foo.That:
drinkThat();
break;
}
... should perhaps be rewritten to as ...
……或许应该改写为……
IAbstract
{
void eat();
void drink();
}
class This : IAbstract
{
void eat() { ... }
void drink() { ... }
}
class That : IAbstract
{
void eat() { ... }
void drink() { ... }
}
However, oneswitch statement isn'timo such a strong indicator that the switch statement ought to be replaced with something else.
然而,一个switch 语句并不是一个强烈的指示,即 switch 语句应该被其他东西替换。
回答by ididak
The best way to handle this particular problem: serialization and protocols cleanly is to use an IDL and generate the marshaling code with switch statements. Because whatever patterns (prototype factory, command pattern etc.) you try to use otherwise, you'll need to initialize a mapping between a command id/string and class/function pointer, somehow and it 'll runs slower than switch statements, since compiler can use perfect hash lookup for switch statements.
处理这个特定问题的最佳方法:干净地序列化和协议是使用 IDL 并使用 switch 语句生成封送处理代码。因为无论您尝试以其他方式使用什么模式(原型工厂、命令模式等),您都需要以某种方式初始化命令 ID/字符串和类/函数指针之间的映射,并且它会比 switch 语句运行得慢,因为编译器可以对 switch 语句使用完美的哈希查找。
回答by Jonas K?lker
The command can be one of 100 different commands
命令可以是 100 个不同命令之一
If you need to do one out of 100 different things, you can't avoid having a 100-way branch. You can encode it in control flow (switch, if-elseif^100) or in data (a 100-element map from string to command/factory/strategy). But it will be there.
如果你需要做 100 件不同的事情中的一件,你就无法避免拥有 100 路分支。您可以在控制流(开关、if-elseif^100)或数据(从字符串到命令/工厂/策略的 100 元素映射)中对其进行编码。但它会在那里。
You can try to isolate the outcome of the 100-way branch from things that don't need to know that outcome. Maybe just 100 different methods is fine; there's no need to invent objects you don't need if that makes the code unwieldy.
您可以尝试将 100 路分支的结果与不需要知道该结果的事物隔离开来。也许只有 100 种不同的方法就可以了;如果这会使代码变得笨拙,则无需发明不需要的对象。
回答by cyclo
Yes, I think large case statements are a symptom that one's code can be improved... usually by implementing a more object oriented approach. For example, if I find myself evaluating the type of classes in a switch statement, that almost always mean I could probably use Generics to eliminate the switch statement.
是的,我认为大 case 语句是可以改进代码的一种症状……通常是通过实现更面向对象的方法。例如,如果我发现自己在一个 switch 语句中评估类的类型,那几乎总是意味着我可以使用泛型来消除 switch 语句。
回答by Rine
You could also take a language approach here and define the commands with associated data in a grammar. You can then use a generator tool to parse the language. I have used Ironyfor that purpose. Alternatively you can use the Interpreter pattern.
您还可以在此处采用语言方法,并在语法中定义具有关联数据的命令。然后,您可以使用生成器工具来解析语言。为此,我使用了Irony。或者,您可以使用解释器模式。
In my opinion the goal is not to build the purest OO model, but to create a flexible, extensible, maintainable and powerful system.
在我看来,目标不是构建最纯粹的 OO 模型,而是创建一个灵活、可扩展、可维护且功能强大的系统。
回答by devio
I'd say that the problem is not the big switch statement, but rather the proliferation of code contained in it, and abuse of wrongly scoped variables.
我想说问题不在于大的 switch 语句,而在于其中包含的代码激增,以及滥用范围错误的变量。
I experienced this in one project myself, when more and more code went into the switch until it became unmaintainable. My solution was to define on parameter class which contained the context for the commands (name, parameters, whatever, collected before the switch), create a method for each case statement, and call that method with the parameter object from the case.
我自己在一个项目中经历过这种情况,当时越来越多的代码进入交换机,直到它变得无法维护。我的解决方案是定义包含命令上下文的参数类(名称、参数等,在切换之前收集),为每个 case 语句创建一个方法,并使用 case 中的参数对象调用该方法。
Of course, a fully OOP command dispatcher (based on magic such as reflection or mechanisms like Java Activation) is more beautiful, but sometimes you just want to fix things and get work done ;)
当然,一个完全 OOP 的命令调度程序(基于诸如反射之类的魔法或 Java Activation 之类的机制)更漂亮,但有时您只想解决问题并完成工作;)