-
28-09-2019 - |
题
假设我们有一个抽象课 Element
从哪个课程 Triangle
和 Quadrilateral
是从。
假设这些类是与取决于元素形状的插值方法结合使用的。因此,基本上我们创建了一个抽象类 InterpolationElement
我们得出的 InterpolationTriangle
和 InterpolationQuadrilateral
.
然后,将插值功能包括在 Triangle
和 Quadrilateral
课程,我们在类中添加一个const-Reference数据成员 Element
类型 InterpolationElement
, , 那是:
class Element
{
public:
Element(const InterpolationElement& interp);
const InterpolationElement& getInterpolation() const;
private:
const InterpolationElement& interpolation;
};
然后,我们创建一个方法(如Scott Meyers所述,有效的C ++),可以实现类的本地静态对象 InterpolationTriangle
作为
const InterpolationTriangle& getInterpolationTriangle()
{
static InterpolationTriangle interpolationTriangle;
return interpolationTriangle;
}
这样的课 Triangle
可以像:
class Triangle : public Element
{
public:
Triangle() : Element( getInterpolationTriangle() ) {}
};
这是我的问题:为了在我的班级中纳入插值方法,这种方法是否正确 Element
?这是在专业场景中使用的吗?
我可以直接在类上实现所有插值方法 Element
(纯虚拟),并在派生类中覆盖它们 Triangle
和 Quadrilateral
. 。但是,在我看来,这种方法似乎很麻烦,因为每次我需要改进或实施新的插值功能时,我都必须在这些课程上这样做。此外,使用这种方法会变得越来越大(许多方法)。
我想听听您的一些技巧和评论
提前致谢。
额外细节:
class InterpolationElement
{
public:
InterpolationElement();
virtual double interpolationMethod1(...) = 0;
:
virtual double interpolationMethodN(...) = 0;
}
class InterpolationTriangle : public InterpolationElement
{
public:
InterpolationTriangle () {}
virtual double interpolationMethod1(...) { // interpolation for triangle }
:
virtual double interpolationMethodN(...) { // interpolation for triangle }
}
class InterpolationQuadrilateral : public InterpolationElement
{
public:
InterpolationTriangle () {}
virtual double interpolationMethod1(...) { // interpolation for quadrilateral}
:
virtual double interpolationMethod1(...) { // interpolation for quadrilateral}
}
解决方案
这些类与插值结合使用 方法. 。为什么这些方法需要在单例对象中?这里的单身人看起来很成问题。
class Element
{
public:
virtual double interpolationMethod1(...) = 0;
:
virtual double interpolationMethodN(...) = 0;
};
class Triangle : public Element
{
public:
virtual double interpolationMethod1(...) { // interpolation for triangle }
:
virtual double interpolationMethodN(...) { // interpolation for triangle }
}
另外,欢迎来到!
其他提示
这让人想起我回答的问题 这里. 。关于数据容器和策略的分离的相同想法。
您的建议存在一个小问题:您已在基类中添加了与插值相关的方法,并且您已经更改了构造函数...
因此,首先,如果您想这样做,这就是您应该做的:
class Element
{
public:
private:
// similar signature to a `clone` method
virtual InterpolationElement* interpolation() const = 0;
};
class Triangle
{
public:
private:
virtual InterpolationTriangle* interpolation() const
{
return new InterpolationTriangle();
}
};
这里有2个优势:
- 不再需要更改每个派生对象的构造函数
- 策略对象不再
const
, ,这使其可以在计算过程中保持状态……就像对当前对象插值的引用一样。
但是,这仍然需要更改 Element
类及其每个派生类。不打扰你;)吗?
好吧,现在是时候(一次)呼吁设计模式: Visitor
.
这与策略想法有所不同,依靠双重调度来正常工作。但是,它允许您调整 Element
S一次(有一个 accept
方法),然后添加尽可能多的操作。那太好了。
您总是可以用模板弄乱一点。首先,我们有顶级班级。
class Element {
public:
virtual void calculate() const = 0;
};
...但是然后,我们在层次结构中间也有一个类,这实际上是一个模板。模板不能成为顶级类,因为具有不同参数的模板是不同的类。这个想法是,我们将插值类作为元素的类型参数。
template <typename Interpolation>
class Element_Impl : public Element {
protected:
Interpolation m_interpolation;
};
和插值类。注意,他们不是兄弟姐妹,因为他们不需要。
class InterpolationTriangle {
public:
double interpolate(double a, double b) const {
std::cout << "interpolation triangle" << std::endl;
}
};
class InterpolationQuadrilateral {
public:
double interpolate(double a, double b) const {
std::cout << "interpolation quadrilateral" << std::endl;
}
};
最后,真实元素和小主要过程。
class Triangle : public Element_Impl<InterpolationTriangle> {
public:
void calculate() const {
m_interpolation.interpolate(1.0, 2.0);
}
};
class Quadrilateral : public Element_Impl<InterpolationQuadrilateral> {
public:
void calculate() const {
m_interpolation.interpolate(2.0, 3.0);
}
};
int main() {
const Element &a = Triangle();
const Element &b = Quadrilateral();
a.calculate();
b.calculate();
}
概括:
- 如果需要,您可以轻松地为每个元素切换插值类。
- 在Matthieu的示例中,没有双重vtable访问(首先是计算元素的计算,然后是Interpolationelement的插曲方法)。每个元素在编译时间都知道它正在使用的插值类。
- element_impl是一个丑陋的位,但它使我们脱离了复型。您可以通过实施插值方法包装器进一步扩展它
- http://en.wikipedia.org/wiki/curiye_recurring_template_pattern
一种方法是使用静态方法,并在element_impl中定义包装器 - 仅在一个地方。
class Element {
public:
virtual void calculate() const = 0;
};
template <typename Interpolation>
class Element_Impl : public Element {
protected:
void interpolate(double, double) const {
Interpolation::interpolate(1, 1);
}
};
class InterpolationTriangle {
public:
static double interpolate(double a, double b) {
std::cout << "interpolation triangle" << std::endl;
}
};
class InterpolationQuadrilateral {
public:
static double interpolate(double a, double b) {
std::cout << "interpolation quadrilateral" << std::endl;
}
};
class Triangle : public Element_Impl<InterpolationTriangle> {
public:
void calculate() const {
interpolate(1.0, 2.0);
}
};
class Quadrilateral : public Element_Impl<InterpolationQuadrilateral> {
public:
void calculate() const {
interpolate(2.0, 3.0);
}
};
int main() {
const Element &a = Triangle();
const Element &b = Quadrilateral();
a.calculate();
b.calculate();
}
我首先想到的是 GOF设计模式访问者
据我了解您的问题,这种模式可以准确地解决此问题。
每个访问者对象都定义一种插值技术或用于应用于对象的算法。
因此,元素类在每个新功能时根本不会增长。一旦到位,访问者模式就可以丰富功能,而无需触摸基类定义。