问题描述
这里有一个简单的例子:(我希望 - 请纠正我,如果我错了)战略模式:有一个类 FileWriter
它将键值对写入文件,并使用 IFormatter
接口的对象来格式化正在写入的文本。当创建 FileWriter
时,会传递不同的格式化程序实现和格式化对象。
这是一个(坏的)这种模式的实现:
#include< iostream>
#include< fstream>
#include< stdlib.h>
#include< sstream>
using namespace std;
类IFormatter {
public:
虚拟字符串格式(字符串键,双值)= 0;
};
class JsonFormatter:public IFormatter {
public:
string format(string key,double value){
stringstream ss;
ss<< \+ key +\:<值;
return ss.str();
}
};
class TabFormatter:public IFormatter {
public:
string format(string key,double value){
stringstream ss;
ss<< key +\t<<值;
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_;
};
可以看出,这种方法的主要缺点是不可靠 - Formatter
传递给的对象FileWriter
必须存在于整个 FileWriter
直接导向
SegFault
。
在这方面,我想讨论一下什么可能是用易于使用和简单需求实现这种方法的其他选项: / p>
- 创建文件编写器时可传递新的格式化程序,或
- 并使用。
我想出了以下描述的其他缺点(IMO):
- 模板:具有
FileWriter
FormatterClass 作为参数; 难以调用:FileWriter< JsonFormatter>(test.txt,JsonFormatter
原始指针: - 共享指针:
FileWriter(test.txt,dynamic_pointer_cast< IFormatter *>(shared_ptr< JsonFormatter *>(new JsonFormatter )))
; 缺点:丑陋的调用,再次,如果formatter是在创建文件编写器之前创建的?
FileWriter(test.txt,new JsonFormatter ))
; 缺点 - 谁应该删除formatter对象? FileWriter
?如果是,则传递现有格式化程序的地址将导致 SegFault
一次 FileWriter
对象尝试删除格式化程序。 li> 这里最好的做法是什么?
UPDATE
响应建议使用 std :: function
的答案 - 如果Formatter可以存储状态(比如说,精度)并且有其他方法 getHeader()
,例如,对于CSV文件?
/ code> by value是不可能的,因为它是一个抽象类。
JsonFormatter formatter;
FileWriter writer(test.txt,formatter);
//使用writer。
另一个更好的选择是有一个克隆)
函数 IFormatter
。然后, 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 $ b {
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
对象也。
FileWriter writer(test.txt,JsonFormatter());
//使用writer。
Here's a simplified example of what is called (I hope - please, correct me if I'm wrong) Strategy pattern: there's a class FileWriter
which writes key-value pairs to a file and uses object of IFormatter
interface for formatting text being written. There are different formatters implementations and formatter object is passed when FileWriter
is created.Here's one (bad) implementation of such pattern:
#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_;
};
As can be seen, the main drawback of such approach is it's unreliability - Formatter
object passed to FileWriter
has to exist during whole FileWriter
's lifetime, thus calls like FileWriter("test.txt", JsonFormatter())
lead directly to SegFault
.
In this regard, I'd like to discuss what could be the other options for implementing such an approach with "easy-to-use" and simplicity requirements:
- either new formatter can be passed when file writer is created, or
- existing formatter can be passed and used.
I came up with several alternatives described below with their drawbacks (IMO):
- templates: having
FileWriter
as a template class which takes exactFormatterClass
as an argument; drawback: ugly to call:FileWriter<JsonFormatter>("test.txt", JsonFormatter())
- here,JsonFormatter
is typed twice. - raw pointers:
FileWriter("test.txt", new JsonFormatter())
; drawback - who should delete formatter object?FileWriter
? if yes, then passing an address of existing formatter will lead toSegFault
onceFileWriter
object attempts to delete formatter. - shared pointers:
FileWriter("test.txt", dynamic_pointer_cast<IFormatter*>(shared_ptr<JsonFormatter*>(new JsonFormatter()))
; drawback: ugly to call, and again, what if formatter was created before creating file writer?
What would be the best practices here?
UPDATE
In response to answers that suggested to use std::function
- What if Formatter may store a state (say, precision) and have additional methods, like getHeader()
, for instance, for CSV files?
Additionaly, storing IFormatter
by value isn't possible since it's an abstract class.
The simplest solution is to use:
JsonFormatter formatter;
FileWriter writer("test.txt", formatter);
// Use writer.
The other option that is a little bit better is to have a clone()
function in IFormatter
. Then, FileWriter
can clone the object, take ownership of the clone and delete it in its destructor.
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_;
};
Now, you can call FileWriter
with temporary object too.
FileWriter writer("test.txt", JsonFormatter());
// Use writer.
这篇关于C ++中的策略模式。实施选项的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!