如何实例化基于输入的策略模式

时间:2018-07-24 08:14:14

标签: c++ design-patterns pattern-matching

实例通常忽略了策略模式的实例化。假设有一个输入定义了要使用的类。我们将遵循以下原则:

class Strategy {
    Strategy(){}
    virtual void runAlgorithm() = 0;
};

class A : public Strategy {
    A () {}
    static bool isA(char input){ return input == 'A'; }
    void runAlgorithm { /* Do A algorithm */ }
};

class B : public Strategy {
    B () {}
    static bool isB(char input){ return input == 'B'; }
    void runAlgorithm { /* Do B algorithm */ }
};

// Other algorithms

Strategy* createStrat(char input){
    Strategy* instance;

    // Define which algorithm to use
    if (A::isA(input)) {
        instance = A();
    } else if (B::isB(input)) {
        instance = B();
    } ...

    // Run algorithm
    instance.runAlgorithm();

    return instance;
}

如您所见,如果我们有多种不同的算法,则if / switch可能会变得很大。是否有一种模式可以使此代码更易于人为解析(即for循环和调用)而无需添加对数组?问题也可以扩展为“如何实例化基于输入的策略模式?”

不限于此代码,因为它只是一个示例。

2 个答案:

答案 0 :(得分:2)

好吧,如果您事先了解所有策略,则可以使用非常简单的元编程递归来自动展开if-else链。我们开始:

#include <string_view>
#include <iostream>
#include <exception>
#include <memory>

struct Strategy {
    Strategy(){}
    virtual void runAlgorithm() = 0;
};

struct A : public Strategy {

    A () {std::cout << "Creating A" << std::endl;}

    static constexpr std::string_view id(){
        return std::string_view("A");
    }

    void runAlgorithm() { /* Do A algorithm */ }
};

struct B : public Strategy {

    B () {std::cout << "Creating B" << std::endl;}

    static constexpr std::string_view id(){
        return std::string_view("B");
    }

    void runAlgorithm() { /* Do B algorithm */ }
};

struct C : public Strategy {

    C () {std::cout << "Creating C" << std::endl;}

    static constexpr std::string_view id(){
        return std::string_view("C");
    }

    void runAlgorithm() { /* Do C algorithm */ }
};

// the if else chains are constructed by recursion
template <class Head, class... Tail>
struct factory {

  static std::unique_ptr<Strategy> call(std::string id) {
      if(Head::id() == id) return std::make_unique<Head>();
      else return factory<Tail...>::call(id);
  }
};

// specialization to end the recursion
// this throws an exception, but you can adapt it to your needs
template <class Head>
struct factory<Head> {

  static std::unique_ptr<Strategy> call(std::string id) {
      if(Head::id() == id) return std::make_unique<Head>();
      else throw std::invalid_argument("The strategy id you selected was not found.");
  }
};

// here is your factory which can create instances of A,B,C based only on the runtime id
using my_factory = factory<A,B,C>;

int main() {

    auto Astrategy = my_factory::call("A");
    auto Bstrategy = my_factory::call("B");
    auto Cstrategy = my_factory::call("C");
    my_factory::call("D"); // exception thrown

}

实时代码here

修改

按照Jarod42的建议进行编辑以解决智能指针和错误检查问题。

答案 1 :(得分:1)

是的,有解决方案。有几种方法可以做到这一点。模板或其他课程:

class StrategyMaker {
public:
     void register(std::funcion<bool(char)> predicate,
                   std::function<std::unique_ptr<Strategy>(char)> factory) {
           mFactories.push_back(std::make_pair(std::move(predicate), std::move(factory)));
     }

     std::unique_ptr<Strategy> create(char ch) {
         for (auto pair & : mFactories) {
             if (pair.first(ch)) {
                 return pair.second(ch);
             }
         }
         return {};
     }

private:
     std::vector<std::pair<std::funcion<bool(char)>,
                           std::function<std::unique_ptr<Strategy>(char)>>> mFactories;
};

StrategyMaker maker;
maker.register([](char ch){ return input == 'A'; },
               [](char ch){ return std::unique_ptr(new A); });

一旦我看过一篇不错的文章,展示了如何使其完全自动化。我已经found something like this(我不是100%确信这是我前一段时间读到的内容)。