我发现自己在我的代码中使用了很多政策,通常我对此非常满意。 但有时我发现自己面临在选择策略和运行时的情况下使用该模式,并且我已经开发了habbits来解决这种情况。通常我会从这样的事情开始:
class DrawArrays {
protected:
void sendDraw() const;
};
class DrawElements {
public:
void setIndices( GLenum mode, GLsizei count, GLenum type, const GLvoid *indices);
protected:
void sendDraw() const;
};
template<class Policy>
class Vertices : public Policy {
using Policy::sendDraw();
public:
void render() const;
};
当在运行时选择策略时,我有不同的选择来解决这种情况。
不同的代码路径:
if(drawElements) {
Vertices<DrawElements> vertices;
} else {
Vertices<DrawArrays> vertices;
}
继承和虚拟通话:
class PureVertices {
public:
void render()=0;
};
template<class Policy>
class Vertices : public PureVertices, public Policy {
//..
};
这两种解决方案对我来说都是错误的。第一个创建了一个难以维护的混乱,第二个引入了虚拟调用的开销,我试图通过首先使用策略来避免。
我错过了正确的解决方案,还是使用了错误的模式来解决问题?
答案 0 :(得分:4)
使用第二个版本。虚拟调用比静态调用更昂贵,因为它们需要额外的指针查找,但如果“sendDraw”执行任何实际绘制,您将不会注意到差异。如果您以后确实遇到性能问题,请使用分析器找出问题所在并进行修复。在(极不可能)虚拟方法调用实际上是性能问题的情况下,您可以尝试使用策略对其进行优化。在此之前,编写最易维护的代码,以便您可以在以后优化开发时间。
记住:过早优化是万恶之源!
答案 1 :(得分:0)
我没有看到第一个有什么问题 - 它对我来说看起来不是一个难以维护的混乱,虽然这里没有足够的代码来确定是否有更好的重构。
答案 2 :(得分:0)
通常,如果您需要在运行时改变行为,则无论是switch / if语句还是虚拟调用,您都必须支付一些管理费用。问题是您需要多少运行时差异。如果您非常有信心只有少量类型,那么switch语句可能非常合适。虚拟呼叫为将来扩展提供了更大的灵活性,但您不一定需要这种灵活性;这取决于问题。也就是说,仍然有很多方法可以实现“切换语句”或“虚拟呼叫”。而不是切换/如果您可以使用Visitor Pattern(更易于维护),而不是虚拟调用,您可以使用函数指针(当类本身没有意义指定在以下调用的行为时)运行)。此外,虽然我不同意作者所说的一切(我认为他人为地使他的想法和OOP相互排斥)你可能会对Data Oriented Programming感兴趣,特别是如果你正在以你的类名建议渲染
答案 3 :(得分:0)
为什么你反对虚拟电话?这笔费用对你来说真的很重要吗?我认为当你通过编写一个接口和不同的实现而不是一些不可读的模板来表达你想要做的事情时,代码会变得更具可读性。
无论如何,为什么要从Vertices
班级继承Policy
?您已经将它作为模板参数。在这里看起来像合成更合适。如果使用继承,则只能有一个非模板类Vertices
并通过传递不同的Policy
对象来更改其行为 - 这是策略模式。
class Policy {
public:
void sendDraw() const =0;
}
class Vertices {
public:
Vertices(Policy * policy) :
: policy(policy)
{
}
void render() {
// Do something with policy->sendDraw();
}
}
答案 4 :(得分:0)
如果您没有将绘制调用放入显示列表,那么在绘制数组数据时必须将其复制出来。 (调用程序阻塞直到GPU完成,或者驱动程序将其从应用程序内存复制到安全的地方。)因此虚拟功能不会成为问题。如果你把它们放在显示列表中,那么虚函数就不会成为问题,因为它只被设置了一次。
无论如何,PC可以非常快速地进行虚拟呼叫。它们不是自由的,这是真的,但如果每帧绘制(比方说)数千个顶点集,那么每次绘制额外的虚函数调用极不可能破坏银行。在提前考虑的所有事情中,避免在虚拟功能设计的各种情况下使用虚拟功能可能是不太重要的事情之一。不必要的虚拟功能值得避免;真正有用的虚拟功能在被证明有罪之前是无辜的......
(每次调用绘制更多顶点并更改着色器,着色器常量,顶点格式和渲染状态设置的频率可能会更高。)