C++ C+中的战略模式+;。实施方案
下面是一个所谓的(我希望-如果我错了,请纠正我)策略模式的简化示例:有一个类C++ C+中的战略模式+;。实施方案,c++,templates,pointers,shared-ptr,strategy-pattern,C++,Templates,Pointers,Shared Ptr,Strategy Pattern,下面是一个所谓的(我希望-如果我错了,请纠正我)策略模式的简化示例:有一个类FileWriter,它将键值对写入文件,并使用IFormatter接口的对象格式化正在写入的文本。有不同的格式化程序实现,在创建FileWriter时传递格式化程序对象。 下面是这种模式的一个(糟糕的)实现: #include <iostream> #include <fstream> #include <stdlib.h> #include <sstream> usi
FileWriter
,它将键值对写入文件,并使用IFormatter
接口的对象格式化正在写入的文本。有不同的格式化程序实现,在创建FileWriter
时传递格式化程序对象。
下面是这种模式的一个(糟糕的)实现:
#include <iostream>
#include <fstream>
#include <stdlib.h>
#include <sstream>
using namespace std;
class IFormatter {
public:
virtual string format(string key, double value) = 0;
};
class JsonFormatter : public IFormatter {
public:
string format(string key, double value) {
stringstream ss;
ss << "\""+key+"\": " << value;
return ss.str();
}
};
class TabFormatter : public IFormatter {
public:
string format(string key, double value) {
stringstream ss;
ss << key+"\t" << value;
return ss.str();
}
};
class FileWriter {
public:
FileWriter(string fname, IFormatter& fmt):fmt_(fmt)
{
f_.open(fname.c_str(), ofstream::out);
}
void writePair(string key, double value)
{
f_ << fmt_.format(key, value);
}
private:
ofstream f_;
IFormatter& fmt_;
};
#包括
#包括
#包括
#包括
使用名称空间std;
类IFormatter{
公众:
虚拟字符串格式(字符串键,双值)=0;
};
类JsonFormatter:公共IFormatter{
公众:
字符串格式(字符串键,双值){
细流ss;
ss最简单的解决方案是使用:
JsonFormatter formatter;
FileWriter writer("test.txt", formatter);
// Use writer.
另一个稍微好一点的选项是在IFormatter
中有一个clone()
函数。然后,FileWriter
可以克隆对象,获得克隆的所有权并在其析构函数中删除它
class IFormatter {
public:
virtual string format(string key, double value) = 0;
virtual IFormatter* clone() const = 0;
};
class FileWriter {
public:
FileWriter(string fname, IFormatter const& fmt):fmt_(fmt.clone())
{
f_.open(fname.c_str(), ofstream::out);
}
~FileWriter()
{
delete fmt_;
}
void writePair(string key, double value)
{
f_ << fmt_->format(key, value);
}
private:
ofstream f_;
IFormatter* fmt_;
};
模板:将FileWriter作为一个模板类,该模板类使用确切的FormatterClass作为参数;缺点:调用FileWriter(“test.txt”,JsonFormatter())很难-在这里,JsonFormatter被键入两次
更多模板
template<class Formatter>
FileWriter<Formatter> makeFileWriter(const std::string& filename, const Formatter& formatter)
{return FileWriter<Formatter>(filename, formatter);}
使用IFormatter-std::函数;
格式化程序应该是一个函数,而不是一个接口
class FormatterProxy : public Formatter
{
public:
FormatterProxy(Formatter& fmt) :
fmt(fmt)
{
}
std::string format(std::string key, double value)
{
return fmt.format(key, value);
}
private:
Formatter& fmt;
};
调用方可以使用std::ref
来保证生存期,如果想要模糊的生存期,可以包装一个共享ptr,或者通过值传递
如果您想要一个更丰富的接口,您可以使用一堆这样的接口,或者编写一个这样的类(通过继承或手动编写notstd::functions
)
按值存储IFormatter fmt;
,使用fmt(a,b)
而不是fmt.format(a,b)
(DRY!)。客户端代码可以根据需要将其设为ref或smart语义
继承作为一种实现细节,而不是驱动您的设计,是一种解放。这就是标准库所做的(例如,std::shared\u ptr
可以使用deleter).Formatter
必须是可复制构造的,显然表达式f为什么它应该是一个函数?如果格式化程序应该保存一些状态,例如,在格式化程序构造过程中指定的双精度,该怎么办?@peet std函数可以做到这一点。按值存储IFromatter
是不可能的,因为它是一个抽象类关于使用std::function
我认为它不适用于这种情况-格式化程序也可以有其他方法(在我更复杂的情况下,格式化程序有方法getHeader()
,例如,它返回CSV文件的头).@peeton MyIFormatter
按值工作。您的解决方案不按值工作。这是您的解决方案的一个问题,因此您的终身管理难题。如果您想摆脱解决方案中的终身管理难题,请停止在接口中烘焙指针:指针(和引用)暗示终身管理问题。您的实际用例(未反映在OP中)需要更多的工作才能在其上使用类型擦除,但这是完全可行的。您认为std::function
无法存储状态的想法……是不正确的。std::function
的泛化解决了您的一般问题。与@Yakk的a相同nswer-如果IFormatter
必须携带一个状态(例如,精度为double)或可能有其他方法(例如,用于CSV文件格式化的getHeader()
),该怎么办?是否应该有不同的设计?只要格式化程序
是可复制构造的,就没有理由不能携带状态。如果需要其他方法,可以传递实现运算符()
的格式化程序,或者将格式化程序包装在lambda:[Formatter](std::string s,double d){return Formatter.format>(s,d);}
(此处,使格式常数或lambda可变
)@peeton我想我知道你说的w.r.t.额外方法是什么意思。我已经更新了我的答案。如果有帮助,请告诉我。虽然这个解决方案会起作用,但事实上,在我看来,所述设计更像是一种通用方法,应该由语言/标准库功能支持(或需要不同的设计/模式)而不是要求程序员通过为他的类编写clone()
方法来显式地处理它。谢谢你的回答!除非找到合适的解决方案,否则我现在还是尽量不使用模板。
auto fileWriter = makeFileWriter("test.txt", JSonFormatter());`
using IFormatter - std::function<std::string(std::string,double)>;
class FileWriter {
public:
template<typename Formatter>
FileWriter(std::string fname, Formatter fmt) :
fmt(fmt)
{
f.open(fname.c_str(), std::ofstream::out);
}
void writePair(std::string key, double value)
{
f << fmt(key, value);
}
private:
std::ofstream f;
std::function<std::string (std::string, double)> fmt;
};
struct Formatter
{
virtual ~Formatter() {}
virtual std::string format(std::string key, double value) = 0;
};
class FileWriter {
public:
FileWriter(std::string fname, std::unique_ptr<Formatter>&& fmt_)
{
if (!fmt_)
{
throw std::runtime_error("Formatter cannot be null");
}
f.open(fname.c_str(), std::ofstream::out);
fmt = std::move(fmt_); // strong exception safety guarantee
}
void writePair(std::string key, double value)
{
f << fmt->format(key, value);
}
private:
std::ofstream f;
std::unique_ptr<Formatter> fmt;
};
class FormatterProxy : public Formatter
{
public:
FormatterProxy(Formatter& fmt) :
fmt(fmt)
{
}
std::string format(std::string key, double value)
{
return fmt.format(key, value);
}
private:
Formatter& fmt;
};