Параметры виртуальной функции используются не во всех подклассах;Есть ли лучший способ дизайна? - PullRequest
4 голосов
/ 14 марта 2012

Я пишу несколько связанных классов C ++, и у меня возникают проблемы с дизайном конкретной унаследованной функции.

В частности, все классы являются «операциями» на деревьях, и мне нужно иметь возможность выполнять набор произвольных операций с деревьями на дереве. У меня есть функция с именем ExecuteOperation () в каждом классе.

Проблема в том, что в некоторых классах мне нужно больше дополнительной информации, чем в других. Прямо сейчас я просто передаю дополнительную информацию всем классам, с абстрактным базовым классом, определенным так:

class BasicOperation  {
    public:
    //...
        virtual void ExecuteOperation(Tree* tree, multimap<int,Foo*> extra1, multimap<int,Foo*> extra2) = 0;
    //...
}

Потомкам подкласса BasicOperation, "SpecialOperation", нужны параметры extra1 и extra2, тогда как потомки "NonspecialOperation" вообще не используют эти параметры.

Вызывающий объект ExecuteOperation обычно выглядит примерно так:

Tree* tree;
std::vector<BasicOperation*> operations;
//...
for(size_t i=0;i<operations.size();i++)  {
    operations[i]->ExecuteOperation(tree,extra1,extra2);
}

Заполнение extra1 и extra2 зависит от конкретного дерева и может потребовать значительных вычислительных ресурсов (лучше не создавать их заново при каждом вызове ExecuteOperation).

Есть ли лучший способ спроектировать это так, чтобы только объекты SpecialOperation получали переданные им параметры? Или это просто нормально, когда неиспользуемые параметры передаются в классы, такие как NonspecialOperation?

Странно определять NonspecialOperation :: ExecuteOperation () с двумя параметрами, которые он не использует.

Единственное, о чем я до сих пор думал, - это чтобы каждый объект SpecialOperation возвращал указатель на вызывающего и сохранял дополнительную информацию в объекте вызывающего. Мне не очень нравится это решение, потому что extra1 и extra2 слишком зависят от текущего состояния; Если не считать проблем с распараллеливанием, то эта установка просто «чувствует себя неправильно».

Кроме того, обтекание дерева, extra1 и extra2 в другой структуре для передачи в ExecuteOperation может сделать его более понятным и имеет смысл, поскольку extra1 и extra2 являются дополнительными дескрипторами для дерева, но я не знаю, является ли это лучшим решением :

struct TreeEx  {
    Tree* tree;
    multimap<int,Foo*> extra1, extra2;
};
//in BasicOperation:
virtual void ExecuteOperation(TreeEx* tree_with_info) = 0;

Ответы [ 2 ]

3 голосов
/ 14 марта 2012

Используйте

virtual void ExecuteOperation (Tree * tree);

Без дополнительных аргументов.

Передача дополнительных аргументов через конструктор в классах, полученных из BasicOperation. Добавьте в дерево методы, которые позволят BasicOperation извлечь необходимые данные.

Также см. Команда шаблон и Посетитель шаблон

- EDIT -

Дополнительные аргументы относятся к дереву

Создайте виртуальную функцию в базовом классе дерева, которая создаст нужную вам команду

class Tree{
public:
    virtual BasicOperation* makeSpecificTreeOperation() = 0;
    virtual ~Tree();
};
  • для каждого типа команды.

Отменить это у детей

 class DerivedTree: public Tree{
    ...
    virtual BasicOperation* makeSpecificTreeOperation();
    ...
 };

 class DerivedCommand: public BasicOperation{
 ....
 public:
    DerivedCommand(ExtraTreeData& extraData);
 };

 BasicOperation* DerivedTree::maksSpecificTreeOperation(){
     return new DerivedCommand(this->extraData);
 }

и вернуть требуемый класс команд, передавая данные в команду с помощью конструктора.

См. Абстрактная фабрика шаблон и Строитель шаблон.

1 голос
/ 14 марта 2012

Чисто иметь ссылку обратно на вызывающую сторону, если она через интерфейс. Таким образом, вы инвертируете зависимость. Вы получите:

virtual void ExecuteOperation(ICaller* caller)

Затем вы можете либо реализовать его в реальном вызывающем (но у вас, очевидно, есть проблемы с параллелизмом в вашем контексте), либо реализовать его в выделенном классе. Вы уменьшаете сцепление таким образом, и это хорошо. Конечно, ICaller предоставляет геттеры для extra1 и extra2, которые будут вызываться только для соответствующих реализаций BasicOperation.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...