【发布时间】:2021-05-02 13:37:41
【问题描述】:
我们将一个实现抽象类 IFunctionality 的对象包装在一个我们正在编写的也实现 IFunctionality 的类中。
IFunctionality 接口是在第三方代码中定义的,目前它只包含虚函数,其中大部分是纯虚函数。非纯虚函数通常有一个空实现,并在具体实现中被覆盖。 IFunctionality 没有任何成员变量(目前)。
我们的包装器看起来像这样:
class WrapperFunctionality : public IFunctionality
{
public:
WrapperFunctionality(IFunctionality& pOriginal)
: m_pOriginal(pOriginal)
{
}
// We override all virtual functions and forward them to the original.
void doX() override { m_pOriginal->doX(); }
void doY() override { m_pOriginal->doY(); }
// ...
// Well, except a few functions that we specialize.
// That's why we need the wrapper.
void doSomethingSpecial() override { ... }
};
目前一切都很好。但是代码将来可能会静默地破坏。
问题 1:第三方代码可能会向 IFunctionality 添加新的非纯虚函数。它将未被检测到,我们将无法在原始对象中调用相应的函数。
问题 2:第三方代码可以将公共成员添加到 IFunctionality 并可以直接访问这些成员。我们也无法将这些修改转发给原始对象。
我想知道我们是否可以借助 static_assert 或一些模板魔法在编译时检测到这些问题。
问题 2 已在 How to detect if a class has member variables? 中讨论,但没有令人满意的解决方案。但在我看来,这不太可能发生(因为这些第三方开发人员似乎并不喜欢这个接口类的公共成员变量)。
但问题 1 更有可能发生。接口类已经有非纯虚函数,并且很可能得到新的。有没有办法强制我的类实现该类的所有虚函数(纯或非纯)?
为了澄清事情,让我对情况进行更具体的描述。
界面表示一个表面,可以在其上绘制具有给定属性的几何图形:
class ISurface
{
public:
virtual void setColor(const RGB& color) = 0;
virtual void setOpacity(float alpha) = 0;
virtual void drawLine(...) = 0;
virtual void drawCircle(...) = 0;
// etc
};
具体的ISurface实现由第三方框架实例化,基于后端有多种实现。
我们的系统中有许多知道如何将自己绘制到 ISurface 的对象。我们不控制他们的draw 功能。对象可以来自第三方插件:
class IDrawableObject
{
public:
virtual void draw(ISurface* pSurface) const = 0;
};
然后是IDrawableObject的可能实现,可能来自第三方代码:
class SomeObject : public IDrawableObject
{
public:
virtual void draw(ISurface* pSurface) const override
{
pSurface->setColor(RGB(255, 0, 0));
pSurface->drawLine(...);
pSurface->setOpacity(0.7f);
pSurface->fillCircle(...);
}
};
在某些时候,我们希望通过覆盖它们的颜色或不透明度来更改这些第三方对象的视觉表示。我们可以挂钩到对对象进行绘图调用的过程:
// That function will be called by the framework
virtual void drawObjectToSurface(const IDrawableObject* pObject, ISurface* pSurface) override
{
pSurface->setColor(m_overrideColor);
pSurface->setOpacity(m_overrideAlpha);
// Next line does not work as expected, because the object
// overwrites our color and alpha before drawing itself
//pObject->draw(pSurface); // does not work
// Instead, we use a wrapper that blocks color and opacity writes
BlockingSurfaceWrapper wrapperSurface(pSurface);
pObject->draw(&wrapperSurface);
}
【问题讨论】:
-
这正是正交概念不应该虚拟实现的原因;它使扩展非常困难。不幸的是,第三方开发商还是这样做了。
-
你能澄清一下吗?这里的正交概念是什么?
-
错字:我说的是“虚拟”,但我的意思是“垂直”。如果有任何正当理由在保留其接口的同时更改
ISurface的实现,那么一开始就应该将接口和实现分开。即ISurface应该只有一个接口(纯虚函数),而其他一些类,比如SimpleSurface,可以提供一些可以共享/继承的基本实现,如果需要的话。这样一来,您的BlockingSurfaceWrapper可以简单地继承ISurface,同时包装SimpleSurface,问题就可以轻松解决
标签: c++ virtual-functions proxy-pattern