Как сохранить абстрактный шаблонный класс с абстрактным типом шаблона в контейнере STL? - PullRequest
0 голосов
/ 01 мая 2018

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

Недавно я пытался реализовать шаблон проектирования "CQRS Command Handler" с использованием C ++. У меня есть 2 иерархии, которые я должен жениться вместе:

  • ICommand

    struct ICommand
    {
        virtual ~ICommand() = default;
    };
    
    struct SampleCommand : ICommand
    {
        int sampleParameter;
    
        SampleCommand() : sampleParameter(0)
        {
        }
    
        explicit SampleCommand(const int sampleParameter)
        {
            this->sampleParameter = sampleParameter;
        }
    };
    
  • ICommandHandler

    template<typename T, typename = std::enable_if_t<std::is_base_of<ICommand, std::decay_t<T>>::value>>
    struct ICommandHandler
    {
         virtual void Handle(std::shared_ptr<T> command) = 0;
         virtual ~ICommandHandler() = default;
    };
    
    class SampleCommandHandler : public ICommandHandler<SampleCommand>
    {
    public:
        void Handle(std::shared_ptr<SampleCommand> command) override
        {
            std::cout << "sampleParameter " << command->sampleParameter << std::endl;
        }
    };
    

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

class Dispatcher
{
public:

template<typename T>
void Dispatch(std::shared_ptr<T> command)
{
    auto handler = std::find_if(std::begin(_handlers), std::end(_handlers), [](auto handler)
    {
        return dynamic_cast<ICommandHandler<T>*>(handler);
    });

    if(handler != std::end(_handlers))
    {
        (*handler)->Handle(command);
    }
}

private:
    std::vector<?> _handlers;
};

Вопрос в том, какого типа должно быть хранилище "_handlers" std :: vector, чтобы метод Dispatcher :: Dispatch работал нормально и возможно ли это?

Что я пробовал до сих пор:

  • std :: vector - не скомпилирован, поскольку приведение конкретного обработчика к ICommandHandler *> невозможно.

    Error   C2440   'initializing': cannot convert from 'SampleCommandHandler *' to 'ICommandHandler<ICommand,void> *'
    
  • std :: vector - не скомпилирован, так как dynamic_cast нельзя применить к void *

Ответы [ 2 ]

0 голосов
/ 01 мая 2018

Конечно, используйте карту или неупорядоченную карту для поиска. Если необходимо, вы можете использовать (неупорядоченный) набор с пользовательским компаратором. Хотя это не отвечает на ваш вопрос: -)

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

Использование std :: shared_ptr означает, что время обработки вашего объекта обработчика гарантируется при обработке события - если оно также принимает shared_ptr, даже если событие удаляется из отображения (либо внутри обработчика, либо в другом потоке или обработчике), но приходит с добавленной внутренней сложностью.

Реализация std :: unique_ptr намного проще и может хорошо работать, когда вы знаете, что обработчик не может быть прерван. Обратите внимание, что клиентский код видит только необработанный указатель - он не должен красть unique_ptr!

0 голосов
/ 01 мая 2018

У вас тут куча бессмысленного (по крайней мере для меня) полиморфизма здесь; Я бы разделил контейнер обработчиков на основе того, что они обрабатывают, вместо того, чтобы использовать один вектор. Как карта / неупорядоченная карта от typeindex до обработчика; или иметь фиксированный набор типов для обработки.

Но для решения проблемы попросили:

struct ICommandHandlerBase{
  virtual ~ICommandHandlerBase(){};
};
template<typename T, typename = std::enable_if_t<std::is_base_of<ICommand, std::decay_t<T>>::value>>
struct ICommandHandler:ICommandHandlerBase
{

теперь хранит вектор ICommandHandlerBase* или unique_ptr<ICommandHandlerBase>.

...