C++ 如何实例化基于输入的策略模式
这些示例通常完全忽略了策略模式的实例化。让我们假设有一个定义要使用哪个类的输入。我们会有一些大致如下的东西:C++ 如何实例化基于输入的策略模式,c++,design-patterns,pattern-matching,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 {
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/开关会变得相当大。是否有一种模式可以使该代码更易于人工解析(即for循环和调用),而无需添加对数组?问题还可以扩展到“如何实例化基于输入的策略模式?”
不要局限于此代码,因为它只是一个示例。好的,如果您事先知道所有策略,您可以使用非常简单的元编程递归自动展开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
}
#包括
#包括
#包括
#包括
结构策略{
策略(){}
虚空运行算法()=0;
};
结构A:公共战略{
A(){std::cout是的,有一个解决方案。有两种方法可以做到这一点。模板就像在其他类中一样,或者这样:
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); });
职业战略制定者{
公众:
无效寄存器(std::function谓词,
标准:功能工厂){
mFactories.push_-back(std::make_-pair(std::move(谓词),std::move(工厂));
}
std::unique_ptr create(char ch){
用于(自动配对和:MFFactory){
if(第一对(ch)){
返回对。第二(ch);
}
}
返回{};
}
私人:
std::载体工厂;
};
战略决策者;
maker.register([](char ch){return input='A';},
[](char ch){return std::unique_ptr(新的A);});
有一次,我看到了一篇很好的文章,展示了如何使它完全自动化。我(我不是100%确定这是我不久前读到的).您正在寻找工厂模式。或多或少。createStat做工厂会做的事情,但工厂不会做得更进一步,这是我的目标。但无论如何,您将有一些选择,std::map
,switch
,或者if
链。一种方法是使用单例映射,然后每当你将策略子类化时,你都会在地图上注册它。这避免了丑陋的if-else链。或者,if-else链可以在编译时轻松创建,而无需展开它们,但你需要事先知道所有策略子类。我对你的评论有点迷茫,@linuxfere。你能假设所有子类都是kno吗提前下载并发布答案?(如果这对我没有帮助,可能会帮助其他人)谢谢你的回答。这似乎是一个很好的方法,但是我从来没有见过模板中使用的保留。你能链接到它们是什么吗?这些是可变模板,看看这里:返回智能指针比原始拥有指针更好。请注意,如果找不到,你选择默认为最后一项,my\factory::call(“D”)
将返回一个C
。我将保留问题,以便可以提出更有趣的答案,但我喜欢这个概念,尽管“晦涩难懂”是的,它肯定可以使用一个智能指针。我发现这个答案比最初的建议稍微复杂一些,因为它从子类中移除了封装(虽然可以很容易地修复)。这里有改进的余地:)这是一个有趣的链接。感谢您将它添加到答案中!