基于策略模式的C++设计问题

9

我一直在阅读Andrei Alexandrescu的《现代C++设计》这本书。我对将一个类分解为策略有疑问。

基本上,什么样的策略大小才是好的?大多数示例显示构建、销毁、线程安全等部分。简而言之,就是小策略 :)

如果我想创建一个文件IO类,并将文件类型作为策略,该怎么办?

struct XX_Type
{
    void  AsyncRead(callback c);
    void* Write(const uint8* image);
}

struct YY_Type
{
    void  AsyncRead(callback c);
    void* Write(const uint8*, image, uint32 offset);
};

template<class FileType = XX_Type>
class File : public FileType
{
    virtual void OnDataRead(const uint8*, uint32 size) = 0;
    ...
};

想法是从文件继承并创建不同的模板,以便在需要时可以生成。这是否适合用于策略,或者我应该只是将文件句柄传递给全局静态函数,还是应该为每种类型的文件创建一个类?我想确保用户错误很少 :),而策略似乎更少出错。
编辑:
感谢@Claudiordgz提供的详细答案
再举个例子就是采用网络方法。
UDP和TCP非常相似,但同时也非常不同。它们都需要套接字,但一个是无连接的,另一个是面向连接的。但从逻辑上讲,它们仍然是传输的一部分,如果我想创建一个更高级别的抽象,比如应用层协议,那么根据@Claudiordgz的说法,至少对我来说,使用传输层作为策略是有意义的,因为它在网络堆栈上的位置。

我认为你的问题过于广泛。对你来说是否有这样的政策是合理的吗?继续吧! - user2033018
@faranwath在SO上,设计问题总是会得到这种反应。个人认为设计方面在这里受到了严重的忽视。这个问题值得一个很好的回答。+1 - David
1个回答

17
政策是一种聪明的开关机制。它们用于以下任何一种情况:
- 带有一个模板工厂的工厂模式,该模板工厂在产品之间切换。 - 带有一个模板类的策略模式,该模板类在行为之间切换。 - 具有不同类型属性包的类。例如,您拥有一辆车,在策略中,您可以具有行为。
引用:
“这个想法是继承文件并创建不同的模板,以后需要时可以生成。这对政策来说是否合适,或者我应该将文件句柄传递给全局静态函数,或者我应该为每种类型的文件创建一个类?我想确保用户错误很低:),而政策似乎不容易出错。”
你可以按你说的方式走,下面是每个方法的优缺点:
- 全局静态函数
优点: - 如果你计划进行小程序那还有什么好担心的?使用它们然后释放并停止开发 缺点: - 您的实现可能需要其他地方的额外数据,这意味着事物可能会变得不成比例。 - 随着程序的增长,您可能希望在全局静态函数之间共享内容,因此您删除静态内容,然后开始使用全局变量,然后是混乱,这需要大约3年的时间。如果适用于您的情况,请注意。 - 你想在一个线程中运行一组函数,在另一个线程中运行另一个函数,因此你创建一个函数管理器类,使用全局静态函数需要进行繁琐的重构。
- 每种文件类型的类
优点: - 有很多关于如何做到这一点的教程 缺点: - 随着程序的增长,要跟踪的类会越来越多。 - 大量的…………模版代码需要重写 - 它基本上与使用模板相同,如果您可以想出一个模板主类,那么某些执行问题就限制在其中,而不是扩散到其余文件中。
- 政策(免责声明:我喜欢政策)
优点: - 统治所有的一个主人接口 - 在编译时创建代码,因此每个使用的代码都为一个类。 - 与许多锅炉板-y类相比,易于维护。 缺点: - 如Alexandrescu所述,一些模板编码可能使编译优化无效。 - 设计更难,理解更难,实现更难。 - 更容易的路径可能会吸引您,您可能会退出实现并像小女孩一样哭着回到全局静态函数。
一个运行方法根据您的算法可以延伸到数千行代码。

大多数示例展示了构造、析构、线程安全等部分。简单来说,这是因为它们只是小的示例,策略只能扩展到你的想象力。请记住,你是程序员,你可以把代码推向前所未有的高度。如果你不这样做,没有人会。

还记得弗罗多... 这项任务是交给你的,如果你找不到方法,就没有人会找到。

###对我来说,至少在网络堆栈中使用传输层作为策略是有意义的。###

例如,假设你有一个名为connection的类。然后你有一个TCP_conn类和一个UDP_conn类,它们各自定义了数据包、头文件和方法,例如:

  • send方法
  • TCP的receive方法
  • setup方法

然后你像你的示例一样继承:

Template<class Protocol>
class Connection : public Protocol
{
   // your inherited methods would be here
   // just define connect or something
}

例如,假设您有一个Waveform类,用于生成波形。
template <class SamplingPolicy >
class Waveform
{
public:
  typedef typename SamplingPolicy::iterator iterator;
  typedef typename SamplingPolicy::const_iterator const_iterator;
  typedef typename SamplingPolicy::inner_iterator inner_iterator;
  typedef typename SamplingPolicy::const_inner_iterator const_inner_iterator;
  typedef typename SamplingPolicy::size_type size_type;
  typedef typename SamplingPolicy::component component;
  typedef typename SamplingPolicy::Wave Wave;

  iterator begin();
  const_iterator begin() const;
  iterator end();
  const_iterator end() const;

  inner_iterator begin(iterator Itr);
  const_inner_iterator begin(iterator Itr) const;
  inner_iterator end(iterator Itr);
  const_inner_iterator end(iterator Itr) const;

  std::size_t Rows() const;
  std::size_t Columns() const;
  const typename SamplingPolicy::Wave& get() const;
  typename SamplingPolicy::Wave& get();

  typename SamplingPolicy::component& row(size_type const &n);
  const typename SamplingPolicy::component& row(size_type const &n) const;

  Waveform();
  ~Waveform();

  template <class Tx, class Ty>
  void setup(Tx const &frequency, Ty const &amplitude);

  template <class T>
  double Omega(T const &frequency);

  Waveform& operator=(Waveform const &rhWave);
  Waveform& operator+=(Waveform const &rhWave);
  Waveform& operator*=(Waveform const &rhWave);
  Waveform& operator-=(Waveform const &rhWave);
  Waveform& operator/=(Waveform const &rhWave);
  template<class T>
  Waveform& operator=(T const &number);
  template<class T>
  Waveform& operator+=(T const &number);
  template<class T>
  Waveform& operator*=(T const &number);
  template<class T>
  Waveform& operator-=(T const &number);
  template<class T>
  Waveform& operator/=(T const &number);

  Waveform operator+(Waveform const &rhWave) const;
  Waveform operator-(Waveform const &rhWave) const;
  template<class T>
  Waveform operator+(T const &number) const;
  template<class T>
  Waveform operator-(T const &number) const;
  Waveform operator/(Waveform const &rhWave) const;
  template<class T>
  Waveform operator/(T const &number) const;
  Waveform operator*(Waveform const &rhWave) const;
  template<class T>
  Waveform operator*(T const &number) const;

  void PrintToConsole(std::size_t columns);
  void PrintToFile(std::string const &filename,
    std::size_t columns);
protected:
  SamplingPolicy _samples;
  double _frequency;
  double _amplitude;
  std::string _frequencyString;
  std::string _amplitudeString;
  std::map<int,double> _SampleTimes;

private:
  bool ValidateSizes(Waveform const &rhWaveform) const;
  void print(std::size_t const &columns,
    std::ostream &output);
};

但是你需要针对128个样本和1024个样本制定政策,你不能在动态分配上执行此操作,因为需要在编译时定义采样率而不是运行时...(在这种情况下用于测试目的)

因此,在每个样本128个点的情况下,政策将如下所示:

class W_128_Samples
{
public:
  static const std::size_t components = 2;
  static const std::size_t halfcycle_samples = 64;

  typedef boost::array< boost::array<int16_t, halfcycle_samples>, components > Wave;
  typedef boost::array<int16_t, halfcycle_samples> component;
  typedef boost::array< boost::array<int16_t, halfcycle_samples>, components >::iterator iterator;
  typedef boost::array< boost::array<int16_t, halfcycle_samples>, components >::const_iterator const_iterator;
  iterator begin();
  const_iterator begin() const;
  iterator end();
  const_iterator end() const;


  typedef int16_t inner_type;
  typedef boost::array<int16_t, components>::size_type  size_type;
  typedef boost::array<int16_t, components>::iterator inner_iterator;
  typedef boost::array<int16_t, components>::const_iterator const_inner_iterator;
  inner_iterator begin(iterator Itr);
  const_inner_iterator begin(iterator Itr) const;
  inner_iterator end(iterator Itr);
  const_inner_iterator end(iterator Itr) const;

  std::size_t Rows() const;
  std::size_t Columns() const;

  component& row(size_type const &n);
  const component& row(size_type const &n) const;

  const Wave& get() const;
  Wave& get();

protected:
  boost::array< boost::array<int16_t, halfcycle_samples>, components > _wave;
};

而在每个样本1024个点的情况下,策略会变成这样:

class W_1024_Samples
{
public:
  static const std::size_t components = 2;
  static const std::size_t halfcycle_samples = 512;
  typedef boost::array< boost::array<int16_t, halfcycle_samples>, components > Wave;
  typedef boost::array<int16_t, halfcycle_samples> component;
  typedef boost::array< boost::array<int16_t, halfcycle_samples>, components >::iterator iterator;
  typedef boost::array< boost::array<int16_t, halfcycle_samples>, components >::const_iterator const_iterator;
  iterator begin();
  const_iterator begin() const;
  iterator end();
  const_iterator end() const;

  typedef int16_t inner_type;
  typedef boost::array<int16_t, components>::size_type  size_type;
  typedef boost::array<int16_t, components>::iterator inner_iterator;
  typedef boost::array<int16_t, components>::const_iterator const_inner_iterator;
  inner_iterator begin(iterator Itr);
  const_inner_iterator begin(iterator Itr) const;
  inner_iterator end(iterator Itr);
  const_inner_iterator end(iterator Itr) const;

  std::size_t Rows() const;
  std::size_t Columns() const;

  component& row(size_type const &n);
  const component& row(size_type const &n) const;

  const Wave& get() const;
  Wave& get();
protected:
  boost::array< boost::array<int16_t, halfcycle_samples>, components > _wave;
};

正如你所看到的,事情可以变得任意长。

另一个重要的事情是,我使用组合而不是继承来实现我的策略,这是Alexandrescu在他与Herb Sutter合著的另一本C++指南书中的建议。

带回家的主要信息是:

你需要尽可能地节省代码--你写的越少,你就会获得更多的价值。但这并不意味着它不难。

所以,再次强调,这取决于你的问题。

例如,要使用我的代码,我会执行以下操作:

Waveform<W_128_Samples> w1;
  w1.setup(60, 1000);
 Waveform<W_1024_Samples> w2;
  w2.setup(60, 1000);

其中60为频率,1000为振幅。两者都将返回数组,只是大小不同。
当你绘制它时,1024大小会更加平滑。
这对于我在c++中直接测试与波形相关的事物非常方便,我可以通过添加或减去不同的波形来组装不同的波形。
#优点是什么?#
好吧,设置方法是Waveform类模板定义的。但是数组保持在策略中,并且策略还有数组和数组大小的访问器。此外,您可以为策略扩展功能。
#为什么不使用继承?#
个人偏好和一些基于某些书籍的原因。只要不在基类功能上发疯,就可以使用继承。
#为什么Policy中有这么多方法?#
因为波形本身是私有的,用户可以创建声音,但不能改变它。
#为什么有这么多重载?#
我想能够像Matlab一样在C++中使用我的Waveforms。
#什么是Policy实现可能变得很大?#
假设您正在实现粒子群优化。您在一个模板中编写PSO,该模板接收数据数组并根据其进行问题优化。
但是您的策略负责每个粒子的权重以及如何从N数据结构传递到数据数组中。
因此,在模板管理器中调用设置方法,就像我说的那样,管理器接收一个数组,但它调用策略的一种方法来接收数组。该策略中的方法将矩阵映射到数组或将数据库映射到数组。
所以现在您需要处理类似于策略和权重中的I/O之类的东西。
这可能会变得非常庞大。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接