通过多个私有继承扩展类 - 这是一件事吗?

时间:2015-11-29 22:08:23

标签: c++ inheritance design-patterns multiple-inheritance

我正在尝试将现有功能封装在一大堆类中,以便可以对其进行统一修改(例如,互斥,优化,记录等)。出于某种原因,我已经把它放到了我的脑海中(多个)私人继承是要走的路,但我无法找到导致我得出这个结论的原因。

问题是:我想做什么的名称是什么,我可以看到它做得对吗?

我认为这不是:

  • 装饰者:我在这个模式中看到的所有描述都包含一个类,以提供从外部看到的额外方法。我想为内部提供功能(提取现有的以及添加其他功能。)
  • 接口:这很接近,因为该功能有一个定义良好的接口(我想模拟测试。)但这个模式再次处理来自外部的视图。

我也愿意接受替代方案,但是这里的大奖是找一篇比我更聪明的人写的文章(a Alexandrescu,Meyers,Sutter等)

示例代码:

// Original code, this stuff is all over
class SprinkledFunctionality
{
  void doSomething()
  {
    ...
    int id = 42;
    Db* pDb = Db::getDbInstance(); // This should be a reference or have a ptr check IRL
    Thing* pThing = pDb->getAThing(id);
    ...
  }
}

// The desired functionality has been extracted into a method, so that's good
class ExtractedFunctionality
{
  void doSomething()
  {
    ...
    int id = 42;
    Thing* pThing = getAThing(id);
    ...
  }

protected:
  Thing* getAThing(int id)
  {
    Db* pDb = Db::getDbInstance();
    return pDb->getAThing(id);
  }
}

// What I'm trying to do, or want to emulate
class InheritedFunctionality : private DbAccessor
{
  void doSomething()
  {
    ...
    int id = 42;
    Thing* pThing = getAThing(id);
    ...
  }
}

// Now modifying this affects everyone who accesses the DB, which is even better
class DbAccessor
{
public:
  Thing* getAThing(int id)
  {
    // Mutexing the DB access here would save a lot of effort and can't be forgotten
    std::cout << "Getting thing #" << id << std::endl; // Logging is easier
    Db* pDb = Db::getDbInstance(); // This can now be a ptr check in one place instead of 100+
    return = pDb->getAThing(id);
  }
}

2 个答案:

答案 0 :(得分:1)

您可能忽略的一个有用技巧是Sutter在其关于虚拟性的着作中创造的非虚拟界面(NVI)。它需要略微反转您查看它的方式,但旨在解决这些精确的问题。它也解决了内部的这些问题,而不是说装饰器,它是关于从外部非侵入地扩展功能。

class Foo
{
public:
    void something()
    {
        // can add all the central code you want here for logging,
        // mutex locking/unlocking, instrumentation, etc.
        ...
        impl_something();
        ...
    }

private:
    virtual void impl_something() = 0;
};

我们的想法是支持公共接口的非虚函数,但要让它们调用在其他地方重写的虚函数(具有私有或受保护的访问权限)。这为您提供了通常可以继承的可扩展性,同时保留了中央控制(否则通常会丢失)。

现在Bar可以从Foo派生并覆盖impl_something以提供特定行为。然而,您保留Foo中的中央控件以添加您喜欢的任何内容并影响Foo层次结构中的所有子类。

最初Foo::something甚至可能不会做任何事情而不是调用Foo::impl_something,但这里的值是呼吸室,它将来会添加你想要的任何中心代码 - 否则就是如果你正在寻找一个直接与虚拟功能有大量依赖关系的代码库,那就太尴尬了。通过依赖于一个依赖于被覆盖的非公共虚拟函数的公共非虚函数,我们获得了一个中间站点,我们可以添加我们喜欢的所有中心代码。

请注意,这也可能过度。 SE中的一切都可能过度,因为如果它只使用全局变量和一个大的main函数,那么一个简单的程序实际上可能是最容易维护的。所有这些技术都需要权衡利弊,但专业人员的规模,复杂性和不断变化的要求开始超过利弊*。

* 我在你写的其他一个问题中注意到,正确的工具应该没有任何缺点,但一切都有缺点,一切都是权衡。它的优点是否超过了最终决定它是否是一个好的设计决策的缺点,并且远远不容易在远见中实现所有这些而不是后见之明。

至于你的例子:

// What I'm trying to do, or want to emulate
class InheritedFunctionality : private DbAccessor
{
  void doSomething()
  {
    ...
    int id = 42;
    Thing* pThing = getAThing(id);
    ...
  }
}

......这里的耦合比这个例子所需要的要严重得多。它可能比你所展示的更多,这使得私有继承成为必需,但是否则组合通常会在没有太多额外努力的情况下大大放松耦合,如下所示:

class ComposedFunctionality
{
  ...
  void doSomething()
  {
    ...
    int id = 42;
    Thing* pThing = dbAccessor.getAThing(id);
    ...
  }
  ...

private:
  DbAccessor dbAccessor;
};

答案 1 :(得分:0)

基本上你正在做的是将getAThing与你doSomething的方式脱钩。看起来很像Factory Method面向对象的设计模式。看看这里: Factory Method Pattern