我有一个 C++ 基类 CAbstrInstruction 和大量的直接子类:
class CAbstrInstruction { /* ... */ };
class CSleepInstruction: public CAbstrInstruction { /* ... */ };
class CSetInstruction: public CAbstrInstruction { /* ... */ };
class CIfInstruction: public CAbstrInstruction { /* ... */ };
class CWhileInstruction: public CAbstrInstruction { /* ... */ };
// ...
还有一个 CScriptWorker 公开了一个公共(public)方法 execute:
class CScriptWorker
{
public:
void execute (const CAbstrInstruction *pI);
private:
void doSleep (const CSleepInstruction *pI);
void doSet (const CSetInstruction *pI);
void doIf (const CIfInstruction *pI);
void doWhile (const CWhileInstruction *pI);
// ...
};
execute 方法的实现目前看起来是这样的:
void CScriptWorker::execute (const CAbstrInstruction *pI)
{
const CSleepInstruction *pSleep =
dynamic_cast<const CSleepInstruction *>(pI);
if (pSleep != NULL)
{
doSleep (*pSleep);
return;
}
const CSetInstruction *pSet =
dynamic_cast<const CSetInstruction *>(pI);
if (pSet != NULL)
{
doSet (*pSet);
return;
}
const CIfInstruction *pIf =
dynamic_cast<const CIfInstruction *>(pI);
if (pIf != NULL)
{
doIf (*pIf);
return;
}
const CWhileInstruction *pWhile =
dynamic_cast<const CWhileInstruction *>(pI);
if (pWhile != NULL)
{
doWhile (*pWhile);
return;
}
/* ... */
}
这非常笨拙,需要 O(log(n)) 才能调用正确的私有(private)方法。有没有 有什么设计模式或语言结构可以简化这个吗?
澄清:我可以将私有(private)执行方法do...移动到指令中 类。 execute 方法将简单地变为:
void execute (const CAbstrInstruction *pI) { pI->execute(); }
然而,这不是我想要的。为什么不? 关注点分离:CAbstrInstruction 的实例只是描述要做什么。它们构成了脚本的抽象语法树。已经足够关心了。 CScriptWorker 关心的是实际执行指令所描述的操作。 CScriptWorker 知道脚本运行的上下文。CAbstrInstruction 不应该知道。
最佳答案
如果事情很简单,将execute
方法的实现移动到CAbstrInstruction
的子类中可能就是答案。然而,OP 明确指出 execute
方法应该在 CScriptWorker
中保持独立,以分离了解什么要完成的问题(指令的工作)从如何完成(CScriptWorker
的工作)。这可以通过双重调度来实现,有时也称为访问者模式:
class IInstructionDispatchTarget
{
public:
virtual void onDispatch (const CSleepInstruction &instr) = 0;
virtual void onDispatch (const CSetInstruction &instr) = 0;
};
class CAbstrInstruction
{
public:
virtual void dispatch (IInstructionDispatchTarget &t) const = 0;
};
class CSleepInstruction: public CAbstrInstruction
{
public:
virtual void dispatch (IInstructionDispatchTarget &t) const override
{ t.onDispatch (*this); }
};
class CSetInstruction: public CAbstrInstruction
{
public:
virtual void dispatch (IInstructionDispatchTarget &t) const override
{ t.onDispatch (*this); }
};
class CScriptWorker: public IInstructionDispatchTarget
{
public:
void execute (const CAbstrInstruction *pI)
{ pI->dispatch (*this); }
virtual void onDispatch (const CSleepInstruction &instr) override
{
// do sleep
}
virtual void onDispatch (const CSetInstruction &instr) override
{
// do set
}
};
当 execute
在 CScriptWorker
上被调用时,它会调用指令的 dispatch
方法。作为返回,该指令使用其特定的 this
指针调用分派(dispatch)目标上的 onDispatch
方法,从而调用正确的方法。
IInstructionDispatchTarget
接口(interface)有两个用途。一方面,它确保 CAbstrInstruction
的实例根本不需要知道 CScriptWorker
;他们需要知道的只是界面。另一方面,它允许其他调度目标使用相同的机制,例如在遍历优化 AST 的指令时。
如果 IInstructionDispatchTarget
的存在被认为是不必要的,事情可以稍微简化,如 ROX 的答案所示。
关于c++ - 根据抽象类快速确定子类,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50983868/