Фабричный шаблон и методы std :: bind - PullRequest
0 голосов
/ 08 января 2019

Я реализовал шаблон фабрики в C ++ несколько недель назад, используя следующее руководство: https://www.codeproject.com/Articles/363338/Factory-Pattern-in-Cplusplus

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

Я использую 2 вещи и реализую третье:

1) Действия: каждое из них представлено в виде класса и содержит статический метод AAction :: ptr create (void * buff). Действия наследуются от класса AAction. Действия могут быть сериализованы с использованием их собственного внутреннего метода serialize () или десериализованы с использованием их статического метода create (buff). Аргумент буфера содержит идентификатор и пароль, необходимые для вызова конструктора LoginAction ().

class AAction {
    typedef std::unique_ptr<AAction> ptr;
};

class LoginAction : public AAction {
private:
    std::string id;
    std::string password;
    bool authenticated;

public:
    LoginAction(std::string id, std::string password);
    virtual ~LoginAction() = default;

    void execute();

    static AAction::ptr create(const void* buffer);
};

2) ActionFactory: используется для десериализации входящих действий путем вызова соответствующего статического метода create () из правильного класса.

class ActionFactory {
    typedef std::unique_ptr<AAction> (*CreateActionFn)(const void*);
    typedef std::map<RawActionType, CreateActionFn> RawFactoryMap;

    RawFactoryMap rawFactoryMap;
public:
    ActionFactory(Authenticator& authenticator) {
    this->registerMethod(RawActionType_RawLoginAction, &LoginAction::create);
    this->registerMethod(RawActionType_RawLogoutAction, &LogoutAction::create);
    this->registerMethod(RawActionType_RawStandAction, &StandAction::create);
    }

    void registerMethod(const RawActionType &rawActionType, CreateActionFn pfnCreate);

    std::unique_ptr<AAction> getAction(RawActionType rawActionType, const void* buffer);
};

Действия могут быть выполнены в любое время в коде, просто вызывая метод execute () без параметров.

До этого момента все работало нормально. Проблема в том, что мне теперь нужно добавить еще несколько параметров к действиям, которые не хранятся внутри пароля. Например, в моем случае: Аутентификатор

3) Аутентификатор, для аутентификации пользователя.

Чтобы внутри LoginAction :: execute () все, что мне нужно сделать, это вызвать

this->authenticator.authenticate(this->id, this->password).

Вот изменения, которые я сделал для этого:

  • Я добавил аутентификатор в конструктор LoginAction: LoginAction(Authenticator& authenticator, std::string id, std::string password);

    и поле: Authenticator& authenticator;

  • Я добавил аутентификатор в метод LoginAction :: create static:

    static AAction::ptr create(const void* buffer, Authenticator& authenticator);

  • Я изменил в конструкторе ActionFactory способ регистрации метода, используя std :: bind:

    this->registerMethod(RawActions_RawLoginAction, std::bind(&LoginAction::create, std::placeholders::_1, authenticator);

Но, поскольку мои типы функций изменились, я больше не могу сохранять их в RawFactoryMap.

ошибка: недопустимое приведение типа ‘std :: _ Bind_helper () (const void , Authenticator &), const std :: _ Placeholder <1> &, Аутентификатор &> :: type {aka std :: _ Bind ( (std :: _ Placeholder <1>, Аутентификатор)) (const void , Аутентификатор &)>} ’для ввода ‘ActionFactory :: CreateActionFn {он же std :: unique_ptr () (постоянный пустота )}»

Каков наилучший способ сохранить карту функций в ActionFactory и соблюдать шаблон Factory?

Заранее спасибо, и хорошего дня!

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

1 Ответ

0 голосов
/ 08 января 2019

обо всем по порядку. С C ++ 11, настоятельно предпочитаем using typedef. Рассмотрим разницу в удобочитаемости между:

typedef std::unique_ptr<AAction> (*CreateActionFn)(const void*);
typedef std::map<RawActionType, CreateActionFn> RawFactoryMap;

и

using CreateActionFn = std::unique_ptr<AAction>(*)(const void*);
using RawFactoryMap = std::map<RawActionType, CreateActionFn>;

Приятно, когда имена появляются слева, а не в произвольном месте.

<ч />

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

using CreateActionFn = std::function<std::unique_ptr<AAction>(const void*)>;

Это будет соответствовать любому вызываемому объекту, который можно скопировать и который вы можете вызвать с помощью const void* и получить unique_ptr<AAction>.

<ч />

И пока мы здесь, не используйте std::bind:

 std::bind(&LoginAction::create, std::placeholders::_1, authenticator)

используйте лямбду:

[authenticator](void const* ptr){ return LoginAction::create(ptr, authenticator); }

или

[=](void const* ptr){ return LoginAction::create(ptr, authenticator); }

Возможно, он не короче, но его немного легче читать.

...