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

Policy Based Design C++ Questions

本文关键字:C++ 问题 于策略 策略      更新时间:2023-10-16

我一直在读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,至少对我来说,使用传输层作为策略是有意义的,因为它在网络堆栈中的位置。

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

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

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

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

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

运行方法可以扩展到数千行代码,具体取决于您的算法。

"大多数例子都显示了诸如构建、破坏、线程安全等部分。简单地说,就是小政策";,这是因为它们只是小例子,策略只能扩展到你的想象,记住你是程序员,你把代码带到了前所未有的高度。如果你不这么做,没有人会这么做。

记住佛罗多这项任务是指定给你的,如果你找不到方法,没有人会

###至少对我来说,将传输层作为一种策略是有意义的,因为它在网络堆栈中的位置

举个例子,假设您有一个名为Class的连接。然后有一个TCP_conn类和UDP_conn类,它们分别定义数据包、标头和方法,例如:

  • 方法send
  • 方法receive(用于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点的情况下,策略看起来像:

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一样进行处理。

它可能会变得巨大。