我最近得知开关语句在OOP中很糟糕,特别是Robert Martin的“Clean Code”(p37-39)。但是考虑一下这个场景:我正在写一个游戏服务器,接收来自客户端的消息,其中包含一个表示玩家行为的整数,例如移动,攻击,挑选物品等等,将会有超过30个不同的行动。当我编写处理这些消息的代码时,不管我想到什么解决方案,它都必须在某处使用切换。如果没有切换语句,我应该使用什么模式?开关语句不好?
开关语句不好?
回答
交换机就像任何其他控制结构。有些地方是最好的/最干净的解决方案,还有更多的地方完全不合适。这只是比其他控制结构更多的滥用方式。
在OO设计中,在像你这样的情况下,通常认为使用不同的消息类型/继承自公共消息类的类,然后使用重载方法“自动”区分不同类型。
在像您这样的情况下,您可以使用映射到您的操作代码的枚举,然后将属性附加到每个枚举值,这样您就可以使用泛型或类型构建来构建不同的Action子类对象,重载方法将起作用。
但这是一个真正的痛苦。
评估是否有设计选项,例如您的解决方案中可行的枚举。如果不是,只需使用开关。
我想你的意思是动态多态而不是静态多态(*重载的方法*) – Geek 2013-03-17 16:26:18
@Toby您能否用下面的例子来详细说明这个语句:“然后给每个枚举值附加一个属性,让您使用泛型或类型构建来构建不同的Action子类对象,以便重载方法可以工作。“? – beinghuman 2017-09-14 06:00:11
我会把这些消息放入一个数组中,然后将该项目与解决方案关键字进行匹配以显示该消息。
并非所有的东西都是OOP。真的很喜欢这个答案。 – grasshopper 2014-02-07 09:57:24
'坏'开关语句通常是那些开关对象类型(或者可能是另一种设计中的对象类型)的开关语句。换句话说,硬编码可能更好地被多态处理。其他类型的开关语句可能是好的
您将需要一个switch语句,但只有一个。当你收到消息时,调用一个Factory对象来返回适当的消息子类(Move,Attack等)的对象,然后调用message-> doit()方法来完成这项工作。
这意味着如果添加更多消息类型,只有工厂对象必须更改。
“Map
从设计模式的角度来看,您可以针对给定的场景使用命令模式。 (见http://en.wikipedia.org/wiki/Command_pattern)。
如果您发现自己在OOP范例中反复使用switch语句,这表示您的类可能设计不好。假设你有一个适当的super和sub类设计和相当数量的多态性。 switch语句后面的逻辑应该由子类来处理。
有关如何删除这些switch语句并介绍正确的子类的更多信息,我建议您阅读Martin Fowler的第一章“重构”。或者你可以在这里找到类似的幻灯片http://www1.informatik.uni-wuerzburg.de/database/courses/pi2_ss03_dir/RefactoringExampleSlides.pdf。 (Slide 44)
IMO switch
陈述不是不好,但应尽可能避免。一种解决方案是使用Map
,其中键是命令,值Command
对象使用方法。或者如果您的命令是数字且没有空白,则为List
。
但是,通常,在实现设计模式时,您会使用switch
语句;一个例子是使用Chain of responsibility模式来处理给定任何命令“id”或“value”的命令。 (也提到了Strategy模式。)但是,就您的情况而言,您还可以查看Command模式。
基本上,在OOP中,您将尝试使用除依赖switch
块之外的其他解决方案,这些块使用过程式编程范例。但是,什么时候以及如何使用都是您的决定。
- 封装是一组具有coherant API类(例如::
Collection
API使用Factory图案等
代码组织的定义是当我个人经常使用
switch
块在许多框架中) - a类是一组相关功能(例如:
Math
类... - 方法是a功能;它应该只做一件事和一件事。 (例如:在列表中添加项目可能需要放大该列表,在这种情况下,
add
方法将依赖于其他方法来执行此操作,并且不会执行该操作本身,因为它不是合同。)
因此,如果您switch
语句执行各种不同的操作,你是“违反”这一定义;而使用设计模式并不是每个操作都在其自己的类中定义的(它是自己的一组功能)。
使用命令。将动作包裹在一个对象中,让多态为你做开关。在C++中(shared_ptr
只是一个指针,或者是Java中的引用。它允许动态分配):
void GameServer::perform_action(shared_ptr<Action> op) {
op->execute();
}
客户挑选要执行的操作,而一旦他们这样做,他们发送的行动本身到服务器,因此服务器不需要做任何分析:
void BlueClient::play() {
shared_ptr<Action> a;
if(should_move()) a = new Move(this, NORTHWEST);
else if(should_attack()) a = new Attack(this, EAST);
else a = Wait(this);
server.perform_action(a);
}
我不买它。这些OOP狂热分子似乎拥有无限RAM和惊人性能的机器。显然,对于无限RAM,您不必担心内存碎片以及连续创建和销毁小助手类时的性能影响。为了解释“美丽的代码”一书的引用 - “计算机科学中的每一个问题都可以用另一个抽象层次来解决”
如果你需要的话,可以使用开关。编译器非常擅长为它们生成代码。
你应该重构然后优化。反过来这样做没有任何意义。 – Eva 2013-01-27 20:30:20
- 1. 开关语句
- 2. Perl开关语句
- 3. PHP开关语句
- 4. 开关语句不被识别
- 5. 开关语句不起作用
- 6. 枚举不为换开关语句
- 7. 开关语句性能C#
- 8. while循环,开关语句
- 9. 开关语句和类
- 10. 开关语句缩进
- 11. SRSS中的开关语句
- 12. 开关与if语句C#
- 13. 开关语句问题
- 14. 凝聚开关语句?
- 15. 虽然,开关,case语句
- 16. 开关语句在php
- 17. 多条件开关语句?
- 18. 开关语句 - 变量“case”?
- 19. 阵列开关case语句
- 20. 大开关语句:坏OOP?
- 21. csh中的开关语句
- 22. 多参数开关语句
- 23. Lisp中的开关语句
- 24. 内部开关语句mpdf
- 25. 关于开关条件语句
- 26. 简化开关语句(Objective-C)
- 27. Java开关语句“case,default expected”
- 28. 随机数和开关语句
- 29. 为什么开关语句在案例
- 30. 替代开关/ if else语句?
请参阅[Large Switch statements:Bad OOP? (http://stackoverflow.com/questions/505454/large-switch-statements-bad-oop) – 2010-12-11 14:01:04
总是觉得这很有趣,因为明显改变的代码是在switch语句中添加另一个案例,但添加另一个类不是。 ..有时OO纯粹主义者可以得到一点“宗教”... – 2010-12-11 14:04:20
'“认为有害的”认为有害的“。 – delnan 2010-12-11 14:04:26