Я знаю, что алмазное наследование считается плохой практикой. Однако у меня есть 2 случая, в которых, как мне кажется, алмазное наследование может очень хорошо вписаться. Я хочу спросить, порекомендуете ли вы мне использовать алмазное наследование в этих случаях, или есть другой дизайн, который мог бы быть лучше.
Случай 1: я хочу создать классы, которые представляют различные виды «Действия» в моей системе. Действия классифицируются по нескольким параметрам:
- Действие может быть «Чтение» или «Запись».
- Действие может быть с задержкой или без задержки (это не просто параметр 1. Это значительно меняет поведение).
- «Тип потока» действия может быть FlowA или FlowB.
Я намерен получить следующий дизайн:
// 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 {/*...*/};
//...
Конечно, я буду подчиняться, что никакие 2 действия (унаследованные от класса 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
};
Опять же, я буду уверен, что никакие 2 класса, унаследованные от класса CommandAbstraction, не будут реализовывать один и тот же метод.
Спасибо.