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

时间:2014-02-25 21:48:21

标签: c++ templates design-patterns

我一直在阅读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提供了一个很好的不确定答案

举另一个例子是采用网络方法。

UPD和TCP非常相似,但同时又非常不同。它们都需要一个套接字,但一个是无连接的,另一个是面向连接的。但从逻辑上讲,它们仍然是传输的一部分,如果我想创建一个更高级别的抽象,说一个应用层协议,所以采取@Claudiordgz它至少对我来说是有意义的使用传输层作为一个政策,因为它放在网络堆栈上。

1 个答案:

答案 0 :(得分:18)

策略是一种巧妙的切换机制,它们用于以下任何一种:

  • 具有一个模板工厂的工厂模式,可在产品之间切换
  • 具有一个模板类的策略模式,可在行为之间切换行为
  • 具有不同类型属性包的类,例如您有车辆,并且在策略中您可以拥有这些行为。

想法是从文件继承并创建一个不同的模板,以后可以在需要时生成。这是否适合策略,或者我应该将文件句柄传递给全局静态函数,还是应该为每种类型的文件创建一个类?我想确保用户错误很低:)并且策略似乎不易出错。

你可以采取任何你刚才所说的方式,这里有各自的优点/缺点:

  • 全局静态函数
    • 赞成
      • 如果你打算做一个小程序那么担心什么?只需使用它们即可发布并停止开发
    • CONS
      • 您的实施可能需要来自其他地方的额外数据,这意味着事情可能会增长不成比例。
      • 随着程序的增长,你可能想要在全局静态函数之间共享东西,所以你删除静态,然后你开始使用全局变量,然后混乱,这需要大约3年才能实现。如果它适用于您的情况,请注意。
      • 你想在一个线程中运行一组函数而在另一个线程中运行另一个函数,因此你创建了一个函数的类管理器,使用全局静态函数,所需的重构可能会让人厌烦。
  • 每种类型文件的类
    • 赞成
      • 关于如何做的大量教程
    • CONS
      • 随着程序增加更多的课程以便跟踪。
      • 重写很多....... BOILERPLATE
      • 它与使用模板基本相同,如果您可以提供模板主类,那么一些执行问题仅限于此而不是传播到其余文件。
  • 政策(免责声明:我喜欢政策)
    • 赞成
      • 一个主要界面来统治所有
      • 在编译时创建代码,因此您为每个使用的代码创建一个类。
      • 与许多锅炉类相比,易于维护。
    • CONS
      • 正如Alexandrescu所说,一些模板编码可能会使编译优化变得无用。
      • 设计难度大,难以理解,难以实施。
      • 更容易的路径可能会引诱您,您可能会退出实施并返回到像小女孩一样哭泣的全局静态功能。

根据您的算法,run方法可能会扩展到数千行代码。

&#34;大多数示例都显示了构造,破坏,线程安全等部分。简单地说是小政策&#34;,这是因为它们是小例子,政策只能扩展到你的想象力,记住你是程序员,你把代码带到没有人做过的高度。如果你不这样做,没有人愿意。

还记得佛罗多...... 此任务是由您指定的,如果您找不到方法,则没有人愿意。

至少让我有意义的是将传输层用作策略,因为它位于网络堆栈上。

比如说,你有一个名为连接的类。然后你有一个类TCP_conn和类UDP_conn,每个都定义数据包,标题和方法,例如:

  • 方法发送
  • 方法接收(针对TCP)
  • 方法设置

然后你继承了你的例子:

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

例如...... 我们假设您有一个生成波形的波形类

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类模板上定义的。但是数组保留在策略中,以及数组的访问器和数组的大小。此外,您还可以扩展策略的功能。

为什么不继承?

个人偏好以及基于某些书籍的其他一些原因。只要你不对基类功能发疯,就可以使用继承。

为什么政策中有这么多方法?

因为波形本身是私有的,所以用户可以创建声音,但不能改变声音。

为什么这么多重载?

我希望能够像Matlab一样使用我的Waveforms,只能在C ++中使用。

你是什么意思政策实施可能会变得很大

假设您正在实施粒子群优化。您可以在接收数据数组的模板中对PSO进行编码,并根据它来优化您的问题。

但是您的策略负责每个粒子的权重,以及如何从N数据结构传递到数据数组。

所以你在模板管理器中调用setup,就像我说管理器收到一个数组,但是它调用了一个接收数组的策略方法。策略中的此方法将矩阵映射到数组中,或将数据库映射到数组中。

所以现在你需要像策略和权重中的I / O那样进行处理。

它可能变得巨大。