Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/146.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ C+中的战略模式+;。实施方案_C++_Templates_Pointers_Shared Ptr_Strategy Pattern - Fatal编程技术网

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 My
IFormatter
按值工作。您的解决方案不按值工作。这是您的解决方案的一个问题,因此您的终身管理难题。如果您想摆脱解决方案中的终身管理难题,请停止在接口中烘焙指针:指针(和引用)暗示终身管理问题。您的实际用例(未反映在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;
};