Как добавить функциональность из комбинации разных производных классов? - PullRequest
2 голосов
/ 03 августа 2020

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

Например,

class Handler {
public: 
  Data_t dbdata_; 
public:
  virtual void updateFlags() = 0; 
}

class AHandler: public Handler {
.....
public:
  void updateFlags() { dbdata_.flagA = 1; }
}

class BHandler: public Handler {
.....
public:
  void updateFlags() { dbdata_.flagB = 1; }
}

class CHandler: public Handler {
.....
public:
  void updateFlags() { dbdata_.flagC = 1; }
}

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

class ACHandler: public Handler {
.....
public:
  void updateFlags() { dbdata_.flagA = 1; dbdata_.flagC = 1; }
}

class ABCHandler: public Handler {
.....
public:
  void updateFlags() { dbdata_.flagA = 1; dbdata_.flagB = 1; dbdata_.flagC = 1 }
}

Основной код функции будет примерно таким.

void process(Request_t *request)
{
  Handler *handler;
  if (request->flagA)
    handler = new AHandler();
  else if (request->flagB)
    handler = new BHandler();
  ....
  ...
  handler->updateFlags();
}

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

Заранее спасибо.

Ответы [ 2 ]

2 голосов
/ 03 августа 2020

Если это все ваши классы, то я не думаю, что вам нужен какой-либо другой класс, кроме Handler. Просто позвольте Handler обрабатывать все флаги. Так будет легко и просто. Я думаю, вы слишком много думаете об этом. Получите рабочую простую версию, а затем просмотрите ее и решите, нужно ли вам и можете ли ее рефрактировать.

Я не знаю, какого типа ваши Request_t->flag(x) и Data_t->flag(x).

просто сделайте

dbdata_.flagA = request_t.flagA;

dbdata_.flagB = request_t.flagB;

et c. Если вы можете сделать их массивом вместо отдельных переменных для упрощения настройки.

1 голос
/ 03 августа 2020

Вы можете рассмотреть вариант класса на основе политик . Для этого мы определяем как шаблон функции * Variadi c, execute(), так и шаблон класса HandlerHolder, который наследуется от Handler и переопределяет функцию-член updateFlags():

template<typename FlagUpdater, typename... FlagUpdaters>
void execute(Data_t& data) {
    execute<FlagUpdater>(data);
    if constexpr (sizeof...(FlagUpdaters))
        execute<FlagUpdaters...>(data);
}

template<typename... FlagUpdaters>
class HandlerHolder final: public Handler {
public:
    void updateFlags() override {
        if constexpr (sizeof...(FlagUpdaters)) 
            execute<FlagUpdaters...>(dbdata_);
    }
};

В этот вариант c шаблона классов, HandlerHolder, вы можете передавать классы (т. Е. Политики) в качестве аргументов шаблона, которые являются вызываемыми, и устанавливать соответствующие флаги. Оператор вызова функции (т.е. operator()) этих классов политики вызывается в его переопределенной функции-члене updateFlags().

Затем вы должны определить классы политики, например:

struct AFlagSetter {
    void operator()(Data_t& dbdata) const {
        dbdata.flagA = 1;
    }
};

struct BFlagSetter {
    void operator()(Data_t& dbdata) const {
        dbdata.flagB = 1;
    }
};

struct CFlagSetter {
    void operator()(Data_t& dbdata) const {
        dbdata.flagC = 1;
    }
};

Обратите внимание, что вы также можете легко определить политики для очистки флагов, например:

struct CFlagClearer {
    void operator()(Data_t& dbdata) const {
        dbdata.flagC = 0;
    }
};

С помощью псевдонимов типов вы можете ввести имена типов для обработчиков, которые вы искали:

using ACHandler = HandlerHolder<AFlagSetter, BFlagSetter>;
using ABCHandler = HandlerHolder<AFlagSetter, BFlagSetter, CFlagSetter>;
...