不要害怕...
我猜你的问题在于熟悉度,而不是技术。熟悉C++面向对象编程。
C++是一种面向对象的语言
它有多种范式,具有面向对象特征,并且能够支持大多数纯面向对象语言的比较。
不要让“C++中的C部分”使你相信C++不能处理其他范式。 C++可以非常优雅地处理许多编程范式。其中,OOP C++是过程化范式(即上述的“C部分”)之后C++范式中最成熟的。
多态性是可用于生产环境的
没有“微妙的错误”或“不适合生产代码”的事情。有些开发者固守自己的方式,而有些开发者会学习如何使用工具并为每个任务使用最佳工具。
switch和多态性几乎相似...
...但多态性消除了大多数错误。
区别在于您必须手动处理开关,而当您熟悉继承方法覆盖时,多态性更加自然。
对于开关,您将不得不将类型变量与不同类型进行比较,并处理差异。对于多态性,变量本身知道如何行为。您只需以逻辑方式组织变量并覆盖正确的方法。
但是最终,如果您忘记在switch中处理情况,则编译器不会告诉您,而如果派生自未覆盖其纯虚拟方法的类,则会通知您。因此,大多数开关错误都得到避免。
总之,这两个功能都是关于做出选择。但多态使您能够做出更复杂,同时更自然,因此更容易的选择。
避免使用RTTI查找对象的类型
RTTI是一个有趣的概念,可以很有用。但是大多数时候(即95%的时间),方法覆盖和继承将足以满足要求,并且您的大部分代码甚至不应该知道所处理的对象的确切类型,而是相信它会做正确的事情。
如果您将RTTI用作显微镜开关,则会错过重点。
(免责声明:我是RTTI概念和dynamic_cast的忠实粉丝。但是,必须使用正确的工具来处理手头的任务,大多数时候RTTI用作显微镜开关,这是错误的)
比较动态和静态多态
如果您的代码在编译时不知道对象的确切类型,则使用动态多态(即经典继承,虚拟方法覆盖等)
如果您的代码在编译时知道类型,则可以使用静态多态,即CRTP模式http://en.wikipedia.org/wiki/Curiously_Recurring_Template_Pattern
CRTP 可以让你的代码具有动态多态性的特点,但每个方法调用都将被静态解析,这对于一些非常关键的代码非常理想。
生产代码示例
类似于下面的代码(从记忆中提取),在生产环境中得到了应用。
更简单的解决方案是围绕着由消息循环调用的过程进行的(在 Win32 中是 WinProc,但为了简单起见,我编写了一个更简单的版本)。总之,它类似于:
void MyProcedure(int p_iCommand, void *p_vParam)
{
switch(p_iCommand)
{
case COMMAND_AAA: { } break ;
case COMMAND_BBB: { } break ;
case COMMAND_XXX: { } break ;
case COMMAND_ZZZ: { } break ;
default: { } break ;
}
}
每次添加命令都会增加一个case。
问题在于有些命令相似,部分实现是共享的。
因此,混合案例对进化是一种风险。
我通过使用命令模式解决了这个问题,即创建一个基本的Command对象,具有一个process()方法。
因此,我重新编写了消息过程,将危险代码(即玩弄void*等)最小化,并编写它以确保我永远不需要再次触及它。
void MyProcedure(int p_iCommand, void *p_vParam)
{
switch(p_iCommand)
{
case COMMAND:
{
Command * c = static_cast<Command *>(p_vParam) ;
c->process() ;
}
break ;
default: { } break ;
}
}
然后,对于每个可能的命令,我没有在过程中添加代码,也没有混合(更糟糕的是复制/粘贴)来自类似命令的代码,而是创建了一个新命令,并从Command对象或其派生对象之一派生它:
这导致了层次结构(表示为树):
[+] Command
|
+--[+] CommandServer
| |
| +--[+] CommandServerInitialize
| |
| +--[+] CommandServerInsert
| |
| +--[+] CommandServerUpdate
| |
| +--[+] CommandServerDelete
|
+--[+] CommandAction
| |
| +--[+] CommandActionStart
| |
| +--[+] CommandActionPause
| |
| +--[+] CommandActionEnd
|
+--[+] CommandMessage
现在,我所需要做的就是为每个对象覆盖进程。
简单易行,易于扩展。
例如,假设CommandAction应该在三个阶段(“before”,“while”和“after”)中执行其过程。它的代码将类似于:
class CommandAction : public Command
{
virtual void process()
{
this->processBefore() ;
this->processWhile() ;
this->processAfter() ;
}
virtual void processBefore() = 0 ;
virtual void processWhile()
{
}
virtual void processAfter() = 0 ;
} ;
例如,CommandActionStart 可以编码为:
class CommandActionStart : public CommandAction
{
virtual void processBefore()
{
}
virtual void processAfter()
{
}
} ;
正如我所说:如果注释得当,易于理解,并且非常易于扩展。
开关语句被简化到了最少的程度(即类似于if的语法),因为我们仍然需要将Windows命令委托给Windows默认过程,而不需要RTTI(或更糟糕的是,内部RTTI)。
在开关语句中使用相同的代码可能会很有趣,我想(仅从我在工作中看到的“历史”代码数量来判断)。