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



我正试图将现有功能封装在大量的类中,以便对其进行统一修改(例如互斥、优化、日志记录等)。出于某种原因,我已经意识到(多重)私有继承是可行的,但我找不到是什么让我得出了这个结论。

问题是:我想做的事情叫什么名字,在哪里我能看到它做得对?

我认为这不是:

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

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

您可能忽略的一种有用技术是Sutter在其关于虚拟性的著作中创造的非虚拟接口(NVI)。它需要稍微颠倒一下你看待它的方式,但目的是为了解决这些确切的问题。它还从内部解决了这些问题,而不是从外部以非侵入性的方式扩展功能的decorator。

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

基本上,您所做的是将getAThing的方式与doSomething的方式解耦。看起来很像Factory方法面向对象的设计模式。看看这里:工厂方法模式

最新更新