我担心在这个网站的某个地方会回答这样的事情,但我找不到它,因为我甚至不知道如何制定这个问题.所以这就是问题所在:
我有一个体素投掷功能.首先,我计算偏移,角度和东西,然后我做了瞌睡.但是我为每个函数制作了几个版本,因为有时我想复制像素,有时是blit,有时为每个像素点亮3*3平方以获得平滑效果,如果对象调整大小,有时只是将像素复制到屏幕上的n*n像素.功能中心的那个小部分有很多版本.
我可以做什么而不是编写10个仅仅由代码的中心部分不同的相同功能?出于性能原因,将函数指针作为参数传递不是一种选择.我不确定让他们内联会做的伎俩,因为我发送的参数不同:有时我计算体积(Z值),有时我知道像素是从下到上绘制的.
我假设有一些方法可以在C++中使用每个人都知道的东西.请告诉我学习这项工作需要学习的内容.谢谢.
传统的OO方法是模板方法模式和策略模式.
第一个是Vincenzo的答案中描述的技术的扩展:您不是编写简单的非虚拟包装器,而是编写包含整个算法的非虚函数.那些可能不同的部分是虚函数调用.给定实现所需的特定参数存储在提供该实现的派生类对象中.
例如.
class VoxelDrawer {
protected:
virtual void copy(Coord from, Coord to) = 0;
// any other functions you might want to change
public:
virtual ~VoxelDrawer() {}
void draw(arg) {
for (;;) {
// implement full algorithm
copy(a,b);
}
}
};
class SmoothedVoxelDrawer: public VoxelDrawer {
int radius; // algorithm-specific argument
void copy(Coord from, Coord to) {
blit(from.dx(-radius).dy(-radius),
to.dx(-radius).dy(-radius),
2*radius, 2*radius);
}
public:
SmoothedVoxelDrawer(int r) : radius(r) {}
};
Run Code Online (Sandbox Code Playgroud)
这是类似的,但不是使用继承,而是将多态Copier对象作为参数传递给函数.它更灵活,它将您的各种复制策略与特定功能分离,您可以在其他功能中重复使用复制策略.
struct VoxelCopier {
virtual void operator()(Coord from, Coord to) = 0;
};
struct SmoothedVoxelCopier: public VoxelCopier {
// etc. as for SmoothedVoxelDrawer
};
void draw_voxels(arguments, VoxelCopier ©) {
for (;;) {
// implement full algorithm
copy(a,b);
}
}
Run Code Online (Sandbox Code Playgroud)
虽然比传入函数指针更整洁,但模板方法和策略都不可能具有比传递函数指针更好的性能:运行时多态仍然是间接函数调用.
策略模式的现代C++等价物是策略模式.这简单地将运行时多态性替换为编译时多态,以避免间接函数调用和启用内联
// you don't need a common base class for policies,
// since templates use duck typing
struct SmoothedVoxelCopier {
int radius;
void copy(Coord from, Coord to) { ... }
};
template <typename CopyPolicy>
void draw_voxels(arguments, CopyPolicy cp) {
for (;;) {
// implement full algorithm
cp.copy(a,b);
}
}
Run Code Online (Sandbox Code Playgroud)
因为类型扣除,你可以简单地打电话
draw_voxels(arguments, SmoothedVoxelCopier(radius));
draw_voxels(arguments, OtherVoxelCopier(whatever));
Run Code Online (Sandbox Code Playgroud)
NB.我在这里略微不一致:我曾经operator()让我的策略调用看起来像常规函数,但是我的策略的常规方法.只要你选择一个并坚持下去,这只是一个品味问题.
有一个最终机制,它是模板方法的编译时多态版本,并使用奇怪的重复模板模式.
template <typename Impl>
class VoxelDrawerBase {
protected:
Impl& impl() { return *static_cast<Impl*>(this); }
void copy(Coord from, Coord to) {...}
// *optional* default implementation, is *not* virtual
public:
void draw(arg) {
for (;;) {
// implement full algorithm
impl().copy(a,b);
}
}
};
class SmoothedVoxelDrawer: public VoxelDrawerBase<SmoothedVoxelDrawer> {
int radius; // algorithm-specific argument
void copy(Coord from, Coord to) {
blit(from.dx(-radius).dy(-radius),
to.dx(-radius).dy(-radius),
2*radius, 2*radius);
}
public:
SmoothedVoxelDrawer(int r) : radius(r) {}
};
Run Code Online (Sandbox Code Playgroud)
一般来说,我更喜欢策略/策略模式的低耦合和更好的重用,并且只选择模板方法模式,只有在你参数化的顶级算法真正一成不变的时候(即,当你要么重构时)现有的代码或者真的确定你对变异点的分析)并且重用真的不是问题.
如果存在多个变异轴(也就是说,您有多个方法copy,并且希望独立地改变它们的实现),那么使用模板方法也非常痛苦.您要么最终得到代码重复或mixin继承.