问题描述
我一直认为大开关语句是OOP设计不良的症状.过去,我已经阅读了讨论此主题的文章,它们提供了基于高度的OOP方法,通常基于多态性来实例化处理案例的正确对象.
我现在处于一种基于来自TCP插座的数据流的情况下,该数据流由基本的Newline终止命令组成,其次是数据行,然后是终端标记.该命令可以是100个不同命令之一,因此我想找到一种将怪物开关语句减少到更易于管理的方法.
我已经进行了一些谷歌搜索以找到我记得的解决方案,但可悲的是,如今,Google已成为许多无关紧要的荒地.
这种问题有任何模式吗?关于可能实施的任何建议吗?
我的一个想法是使用词典查找,将命令文本与对象类型匹配以实例化.这具有仅创建新对象并在表中插入新命令的新命令/类型的好优势.
但是,这也存在类型爆炸问题.我现在需要100个新类,而且我必须找到一种方法来清洁与数据模型的接口. "一个真实的开关语句"真的是要走的方式吗?
我会感谢您的想法,意见或评论.
推荐答案
您可能会从命令模式中获得一些好处.
对于OOP,您也许可以将几个类似的命令崩溃成一个类,如果行为变化足够小,以避免进行完整的类爆炸(是的,我已经听到了OOP Gurus对此的尖叫声).但是,如果系统已经OOP,并且100+命令中的每一个都是独特的,那么只需使它们成为独特的类并利用继承来合并共同的内容.
如果系统不是OOP,那么我不会仅仅为此添加OOP ...您可以轻松地使用简单的字典查找和函数指针,甚至基于命令名称动态生成的函数调用,取决于语言.然后,您只能将逻辑上关联的功能分组到代表类似命令的集合以实现可管理分离的库中.我不知道这种实现是否有一个好术语...我一直认为它是基于MVC的处理URL的"调度员"风格.
.其他推荐答案
我看到两个切换语句是非OO设计的症状,在这种症状中,可以用多种类型替换为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; }
...也许应该被重写为...
IAbstract { void eat(); void drink(); } class This : IAbstract { void eat() { ... } void drink() { ... } } class That : IAbstract { void eat() { ... } void drink() { ... } }
然而,一个开关语句不是 imo如此强烈的指标,以至于应该用其他东西替换开关语句.
其他推荐答案
该命令可以是100个不同命令之一
如果您需要在100件不同的事情中做一个,则无法避免拥有100个分支.您可以在控制流(开关,如果是elseif^100)或数据(从字符串到命令/出厂/策略的100个元素地图)中对其进行编码.但是它将在那里.
您可以尝试将100个分支的结果与不需要知道结果的事物隔离.也许只有100种不同的方法很好.如果使代码变得笨拙,则无需发明您不需要的对象.
问题描述
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.
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.
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?
I'd appreciate your thoughts, opinions, or comments.
推荐答案
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.
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.
其他推荐答案
I see having two switch 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 (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, one switch statement isn't imo such a strong indicator that the switch statement ought to be replaced with something else.
其他推荐答案
The command can be one of 100 different commands
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.
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.