繁体   English   中英

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

[英]Policy Based Design C++ Questions

我一直在阅读 Andrei Alexandrescu Modern C++ Design 的书。 我有一个关于将类分解为策略的问题。

基本上什么是好的政策规模? 大多数示例都显示了构造、销毁、线程安全等部分。 简单地说就是小政策:)。

如果我想创建一个文件 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 至少对我来说使用传输层作为策略是有意义的,因为它放在网络堆栈上。

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

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

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

您可以按照刚才所说的任何方式进行,以下是每种方式的优缺点:

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

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

“大多数示例都展示了构造、销毁、线程安全等部分。简单地说就是小策略”,这是因为它们是小示例,策略只能扩展到你的想象,记住你是程序员,你把代码带到没有人做过的高度。 如果你不这样做,没有人会这样做。

记住弗罗多……这个任务是交给你的,如果你找不到办法,没人会。

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

例如,假设您有一个名为 connection 的类。 然后你有一个类 TCP_conn 和类 UDP_conn,每个类都定义了数据包、报头和方法,例如:

  • 方法发送
  • 方法接收(用于 TCP)
  • 方法设置

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

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 个点时,Policy 将如下所示:

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 在他的另一本 C++ 指南与 Herb Sutter 书中所激发的。

带回家的主要物品是:

您需要节省代码——您编写的代码越少,您带回家的价值就越大。 但这并不意味着它不会很难。

所以,这取决于你的问题。

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

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

其中 60 是频率,1000 是幅度。 两者都将返回数组,只是大小不同。

绘制图形时,1024 大小会平滑得多。

当直接在 C++ 中测试与波形相关的东西时,这对我来说真的很舒服,我可以通过加法或减法来组合不同的波形。

#有什么好处?#

那么,setup 方法是为 Waveform 类模板定义的。 但是数组保留了策略,以及数组的访问器和数组的大小。 此外,您可以扩展策略的功能。

#为什么不继承?#

基于某些书籍的个人偏好和其他一些原因。 只要您不迷恋基类功能,就可以使用继承。

#为什么Policy中有这么多方法?#

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

#为什么要超载?#

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

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

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

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

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

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

它可能会变得巨大。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM