我知道拥有菱形继承(钻石问题)被认为是不良做法。但是,我有2种情况,我认为菱形继承(钻石问题)非常合适。我想问一下,您是否建议我在这些情况下使用菱形继承(钻石问题),或者是否有其他更好的设计。
情况1:我想创建代表我系统中不同类型的“ Action ”的类。这些操作按几个参数分类:
我打算进行以下设计:
// abstract classes
class Action
{
// methods relevant for all actions
};
class ActionRead : public virtual Action
{
// methods related to reading
};
class ActionWrite : public virtual Action
{
// methods related to writing
};
class ActionWithDelay : public virtual Action
{
// methods related to delay definition and handling
};
class ActionNoDelay : public virtual Action {/*...*/};
class ActionFlowA : public virtual Action {/*...*/};
class ActionFlowB : public virtual Action {/*...*/};
// concrete classes
class ActionFlowAReadWithDelay : public ActionFlowA, public ActionRead, public ActionWithDelay
{
// implementation of the full flow of a read command with delay that does Flow A.
};
class ActionFlowBReadWithDelay : public ActionFlowB, public ActionRead, public ActionWithDelay {/*...*/};
//...
当然,我会服从任何两个 Action (从Action类继承)来实现相同的方法。
情况2:我在系统中实现了“命令”的复合设计模式。可以读取,写入,删除命令等。我还希望拥有一系列命令,也可以读取,写入,删除命令等。命令序列可以包含其他命令序列。
所以我有以下设计:
class CommandAbstraction
{
CommandAbstraction(){};
~CommandAbstraction()=0;
void Read()=0;
void Write()=0;
void Restore()=0;
bool IsWritten() {/*implemented*/};
// and other implemented functions
};
class OneCommand : public virtual CommandAbstraction
{
// implement Read, Write, Restore
};
class CompositeCommand : public virtual CommandAbstraction
{
// implement Read, Write, Restore
};
此外,我还有一种特殊的命令,即“现代”命令。一个命令和复合命令都可以是现代命令。处于“现代”状态会将特定属性列表添加到一个命令和复合命令中(两者的属性几乎相同)。我希望能够持有一个指向CommandAbstraction的指针,并根据所需的命令类型对其进行初始化(通过new)。所以我想做以下设计(除了上面的):
class ModernCommand : public virtual CommandAbstraction
{
~ModernCommand()=0;
void SetModernPropertyA(){/*...*/}
void ExecModernSomething(){/*...*/}
void ModernSomethingElse()=0;
};
class OneModernCommand : public OneCommand, public ModernCommand
{
void ModernSomethingElse() {/*...*/};
// ... few methods specific for OneModernCommand
};
class CompositeModernCommand : public CompositeCommand, public ModernCommand
{
void ModernSomethingElse() {/*...*/};
// ... few methods specific for CompositeModernCommand
};
再次,我将确保没有两个继承自CommandAbstraction类的类将实现相同的方法。
谢谢。
最佳答案
继承是C++中第二强的(更多耦合的)关系,仅在友谊之后。如果您可以重新设计为仅使用合成,则您的代码将更松散地耦合在一起。如果不能,则应考虑所有类是否应真正从基类继承。是由于实现还是仅仅是接口(interface)?您是否要将层次结构的任何元素用作基础元素?还是只是您的层次结构中的叶子是真正的Action?如果只有 Action ,而您要添加行为,则可以考虑针对此类行为组合考虑基于策略的设计。
这个想法是,可以在小型类集中定义不同的(正交)行为,然后将其 bundle 在一起以提供真正的完整行为。在该示例中,我将仅考虑定义该操作是现在还是将来执行以及执行命令的策略。
我提供了一个抽象类,以便可以将模板的不同实例化(通过指针)存储在容器中,或作为参数传递给函数并进行多态调用。
class ActionDelayPolicy_NoWait;
class ActionBase // Only needed if you want to use polymorphically different actions
{
public:
virtual ~Action() {}
virtual void run() = 0;
};
template < typename Command, typename DelayPolicy = ActionDelayPolicy_NoWait >
class Action : public DelayPolicy, public Command
{
public:
virtual run() {
DelayPolicy::wait(); // inherit wait from DelayPolicy
Command::execute(); // inherit command to execute
}
};
// Real executed code can be written once (for each action to execute)
class CommandSalute
{
public:
void execute() { std::cout << "Hi!" << std::endl; }
};
class CommandSmile
{
public:
void execute() { std::cout << ":)" << std::endl; }
};
// And waiting behaviors can be defined separatedly:
class ActionDelayPolicy_NoWait
{
public:
void wait() const {}
};
// Note that as Action inherits from the policy, the public methods (if required)
// will be publicly available at the place of instantiation
class ActionDelayPolicy_WaitSeconds
{
public:
ActionDelayPolicy_WaitSeconds() : seconds_( 0 ) {}
void wait() const { sleep( seconds_ ); }
void wait_period( int seconds ) { seconds_ = seconds; }
int wait_period() const { return seconds_; }
private:
int seconds_;
};
// Polimorphically execute the action
void execute_action( Action& action )
{
action.run();
}
// Now the usage:
int main()
{
Action< CommandSalute > salute_now;
execute_action( salute_now );
Action< CommandSmile, ActionDelayPolicy_WaitSeconds > smile_later;
smile_later.wait_period( 100 ); // Accessible from the wait policy through inheritance
execute_action( smile_later );
}
继承的使用允许通过模板实例访问策略实现中的公共(public)方法。由于不允许将新功能成员插入类接口(interface),因此不允许使用聚合来组合策略。在示例中,模板依赖于具有wait()方法的策略,该方法对于所有等待策略都是通用的。现在,等待一个时间段需要通过period()公共(public)方法设置的固定时间段。
在此示例中,“NoWait”策略只是“WaitSeconds”策略的特定示例,其周期设置为0。这是为了标记策略接口(interface)不必相同。通过提供注册为给定事件的回调的类,另一种等待策略的实现可以等待数毫秒,时钟滴答或直到某个外部事件。
如果不需要多态,则可以从示例中取出基类和虚方法。尽管对于当前示例而言,这似乎过于复杂,但您可以决定将其他策略添加到组合中。
如果使用纯继承(具有多态性),则添加新的正交行为将意味着类的数量呈指数增长,但是使用这种方法,您可以单独实现每个不同的部分并将其粘合在Action模板中。
例如,您可以使操作具有周期性,并添加退出策略来确定何时退出周期性循环。首先想到的选项是LoopPolicy_NRuns和LoopPolicy_TimeSpan,LoopPolicy_Until。每个循环都会调用一次此策略方法(在我的情况下为exit())。第一个实现对固定次数(由用户确定,因为在上面的示例中为周期确定)之后退出导出的次数进行计数。第二个实现将在给定的时间段内定期运行该过程,而最后一个实现将运行该过程直到给定的时间(时钟)。
如果您仍在关注我,我确实会做出一些更改。第一个是,我将使用函子和可能使用将命令作为参数执行的模板化构造函数,而不是使用实现方法execute()的模板参数Command。这样做的基本原理是,与boost::bind或boost::lambda等其他库结合使用时,它的可扩展性更高,因为在这种情况下,命令可以在实例化时绑定(bind)到任何自由函数,函子或成员方法。一个类
现在我必须走了,但是如果您有兴趣,我可以尝试发布修改的版本。
关于c++ - 菱形继承(钻石问题)(C++),我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/379053/