2013-07-12 2 views
3

Я проектирую модуль C++. Этот модуль может получать 3 разных типа запросов: Request-A, Request-B и Request-C.
Для каждого типа у меня есть соответствующий класс обработчика: RequestHandler-A, RequestHandler-B и RequestHandler-C (все они реализуют интерфейс IRequestHandler).
Каждый обработчик должен выполнить определенные действия для выполнения своего запроса.
Например, RequestHandler-А должен выполнять их в последовательности:
Действие-1
Действие-2
Действие-3
Действие-4
Действие-5Проектирование классов C++ с частично распространенными реализациями

RequestHandler-B, нужно выполнить эти в последовательности:
Действие 1-
Действие 3-
Действие-5

RequestHandler-с необходимо выполнить их в последовательности:
Действие 4-
Действие 5-

В результате одного действия используются следующим.

Я изо всех сил пытаюсь разработать эти классы, чтобы реализации общих действий были повторно использованы через обработчики. Есть ли какие-либо шаблоны дизайна, которые можно применять здесь? Возможно, шаблон шаблона шаблона может быть возможностью, но я не уверен. Любые предложения были бы весьма признательны.

PS: Чтобы сделать вещи более интересными, есть также требование, когда, если Action-2 терпит неудачу, мы должны повторить его с разными данными. Но, может быть, я слишком далеко задумываюсь.

+2

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

ответ

4

«Общие реализации» означает, что ваше решение не имеет ничего общего с наследованием. Наследование предназначено для интерфейсом повторное использование, а не осуществление повторное использование.

Вы обнаружите, что у вас есть общий код, просто использовать общие функции:

void action1(); 
void action2(); 
void action3(); 
void action4(); 
void action5(); 

struct RequestHandlerA : IRequestHandler { 
    virtual void handle(Request *r) { 
     action1(); 
     action2(); 
     action3(); 
    } 
}; 

struct RequestHandlerB : IRequestHandler { 
    virtual void handle(Request *r) { 
     action2(); 
     action3(); 
     action4(); 
    } 
}; 

struct RequestHandlerC : IRequestHandler { 
    virtual void handle(Request *r) { 
     action3(); 
     action4(); 
     action5(); 
    } 
}; 

Если предположить, что общая функция только внутренние помощники, вы, вероятно, хотите, чтобы сделать их static (или использовать анонимное пространство имен), чтобы получить внутренняя связь.

+1

Это. Или, если выясняется, что 'action2' должен делать в основном одно и то же, но несколько отличается в зависимости от типа запроса/обработчика, то пришло время сделать его шаблоном функции. – aschepler

+0

Спасибо за напоминание, что * Наследование предназначено для повторного использования интерфейса, а не для повторного использования. * – oyenamit

1

У вас может быть один базовый класс, который реализует 5 действий и выводит из него обработчики.

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

+1

В качестве альтернативы, создайте класс-оболочку для каждой функции (или набор функций, то есть 'class ClassAction1 {action1() {}};'), и пусть каждый ребенок наследует любые конкретные функции, в которых они нуждаются. Таким образом, 'RequestHandler-C' даже не получит доступ к функциям, которые он не должен использовать. Хотя все 5 функций в одном базовом классе (при условии, что они защищены) имеют больше смысла. – Suedocode

2

Вы ищете что-то в этом роде?

#include <iostream> 

using namespace std; 

class Interface{ 
    public: 
     void exec(){ 
      //prepare things up 
      vExec(); 
      //check everything is ok 
     }; 
     virtual ~Interface(){} 
    protected: 
     virtual void vExec() = 0; 
     virtual void Action0() = 0; 
     virtual void Action1(){} 
     void Action2(){} 
}; 

void Interface::Action0(){ 
} 

void Action3(){} 

class HandlerA : public Interface{ 
    protected: 
     virtual void vExec(){ 
      Action0(); 
      Action1(); 
      Action3(); 
     } 
     virtual void Action0(){ 
     } 
}; 

class HandlerB : public Interface{ 
    protected: 
     virtual void vExec(){ 
      Action0(); 
      Action1(); 
      Action2(); 
      Action3(); 
     } 
     virtual void Action0(){ 
      Interface::Action0(); 
     } 
}; 

int main() 
{ 
    Interface* handler = new HandlerA(); 
    handler->exec(); 
    HandlerB b; 
    b.exec(); 

    delete handler; 
} 

Как вы можете видеть, что действия могут быть виртуальными членами, невиртуальные члены, свободные функции, или все, что вы могли бы думать, в зависимости от того, что вам нужно.

«Дополнительная» функция подачи действий с различными данными может быть выполнена в exec() (если она является общей) или в vExec (если она специфична для обработчика). Если вы дадите нам более подробную информацию, я могу соответствующим образом изменить этот пример.

Кроме того, вы можете сделать vExec общедоступным и избавиться от exec. Один из примеров - это просто практика, которая мне больше всего нравится (что делает не виртуальные и виртуальные функции интерфейса непубличными).

+0

Это не похоже, что OP хочет, чтобы действия были виртуальными, и в любом случае это не нужно в этом примере. НО, это не очень больно XD. – Suedocode

+0

Это было указано в ответе :) Однако, спасибо за подтверждение! –

+0

LOL yup и, конечно же, он говорит это прямо там. Виноват! – Suedocode

0

Вы считали шаблон дизайна Chain Of Command? http://en.wikipedia.org/wiki/Command_pattern

Это проверенный временем образец, который способствует ослаблению связи между объектами-обработчиками и запросами (командами), которые они получают.

Что вы можете сделать, это перевести объекты запроса в качестве объектов Command. Затем вы указываете, какой тип команд может выполнять каждый из ваших обработчиков. Затем вы можете передать команду обработчикам и передать им команду вперед, если они не могут их обработать. Если обработчик может обрабатывать действие, команда обрабатывается через каждое из своих соответствующих действий. Затем вы можете выполнять каждое логическое действие внутри Handler как объекты самостоятельно, используя композицию.

+0

Я считаю, что вы смешиваете * Command * с шаблоном * Chain of Responsibility *. – oyenamit

Смежные вопросы