c++ - 通过多重私有(private)继承扩展类——这是一回事吗?

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

我正在尝试将现有功能封装在大量的类中,以便可以对其进行统一修改(例如互斥、优化、记录等)出于某种原因,我已经想到了(多个)私有(private)继承是可行的方法,但我找不到导致我得出该结论的原因。

问题是:我想做的事情的名称是什么,我在哪里可以看到它正确完成?

我认为这不是:

  • 装饰器:我看到的所有关于此模式的描述都包装了一个类,以提供从外部查看的额外方法。我想为内部提供功能(提取现有功能并添加其他功能。)
  • 接口(interface):这很接近,因为该功能有一个定义明确的接口(interface)(我想模拟一个用于测试的接口(interface)。)但是这个模式同样处理来自外部的 View 。

我也对其他选择持开放态度,但这里的大奖是找到一篇比我聪明得多的人写的关于它的文章(如 la 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);
  }
}

最佳答案

您可能会忽略的一项有用技术是非虚拟接口(interface) (NVI),这是 Sutter 在其关于虚拟性的著作中创造的。它需要稍微改变您看待它的方式,但旨在解决这些确切的问题。它还从内部解决了这些问题,也就是说,装饰器是关于从外部非侵入性地扩展功能的。

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;
};

我们的想法是支持您的公共(public)接口(interface)使用非虚函数,但让它们调用在其他地方被覆盖的虚函数(具有私有(private)或 protected 访问权限)。这为您提供了通常通过继承获得的可扩展性,同时保留了中央控制(否则通常会丢失)。

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

最初 Foo::something 甚至可能只调用 Foo::impl_something 做任何事情,但这里的值(value)是为将来添加提供的喘息空间任何你想要的中央代码——如果你低头查看一个代码库,它有大量直接依赖于虚拟函数的代码库,这可能会非常尴尬。通过依赖一个公共(public)非虚拟函数,该函数依赖于一个被覆盖的非公共(public)虚拟函数,我们获得了一个中间站点,我们可以在其中添加我们喜欢的所有中央代码。

请注意,这也可能有点矫枉过正。在 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);
    ...
  }
}

...这里的耦合比本示例所需的耦合要紧密得多。它可能比你所展示的更多,这使得私有(private)继承成为必要,但否则组合通常会在没有太多额外努力的情况下大大放松耦合,就像这样:

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

private:
  DbAccessor dbAccessor;
};

关于c++ - 通过多重私有(private)继承扩展类——这是一回事吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33988733/

相关文章:

c++ - gcc:剥离未使用的功能

c++ - native VC++ 使用外部(非项目)dll 引用如何指定 dll 的路径

c++ - 模板令人费解......需要帮助(除了美沙酮)

c# - 如何知道基类中的派生类

java - 准备语句 : create method using an ArrayList of parameters

C++分离数据和逻辑

design-patterns - 是否有任何理由为非常基本的数据对象提供接口(interface)?

c++ - 在 Windows 2000 中使用 Visual Studio 2010 编译的 C++ DLL 时出现问题

java - 对类进行子类化以更改 Kotlin 中类型参数的方差

c# - 从基类创建派生类的实例