编译时接口定义的最佳实践

时间:2012-01-25 22:33:38

标签: c++ interface multiple-inheritance

假设我有以下遗留代码,我有机会改进:

class BaseInterfaceClass
{

#if( HAS_FEATURE_A == 1 )
    void doFeatureA1() = 0;
    void doFeatureA2() = 0;
#endif

#if( HAS_FEATURE_B == 1 )
    void doFeatureB1() = 0;
    void doFeatureB2() = 0;
    void doFeatureB3() = 0;
#endif

#if( HAS_FEATURE_C == 1 )
    void doFeatureC1() = 0;
#endif

    void doBaseFeature1() = 0;
    void doBaseFeature2() = 0;
};

有没有办法取消在编译时定义接口的#ifdef方法,而不会增加新设计的运行时大小?换句话说,我需要保留这样一个事实,即仅使用HAS_FEATURE_A编译的派生类将不包含HAS_FEATURE_BHAS_FEATURE_C的代码(与链接的代码相比,不是实际上是因为运行时检查而使用的。)

一些注释

  1. 功能标志不是互斥的。可以定义它们的任何组合。

  2. 正如我所知,为每个特征定义一个子类,然后使用多重继承将所需的接口组合在一起是不够的。请记住,派生类必须能够在编译时实现任何功能组合,并且不包含任何未为该编译定义的功能。

  3. 更改代码的政治成本意味着我必须能够摆脱所有指令,或者根本不做。换句话说,仅仅移动#ifdef定义以使基类更漂亮是不够的。

  4. 有足够的功能组合,任何蛮力方法都是不现实的。我不打算制作成千上万的子类。

  5. 我不知道模板,但我愿意知道这是否是关键。

3 个答案:

答案 0 :(得分:2)

有办法。但它们真的有用吗?

许多图书馆使用#if条件编译来提出其他选择加入功能。这是一个长期的惯例。

您可以使用混合来“消除”这一点,但不确定代码会因此而变得更具可读性。相反,使用您的代码的人现在必须了解之前发生的事情。

另一方面,由于我们谈论的是接口,您可能很容易将分割接口分成几个组件(如果可能),每个组件都专用于许多功能。知道你是否能够做到这一点需要更多关于班级错综复杂的知识......但它对我来说太像神的对象了。

答案 1 :(得分:1)

在这种情况下,我肯定会避免使用预处理器。

您可以将您的功能分成不同的接口,并让您的派生类选择并确切选择它将实现哪些功能(以及哪些接口)。

然后,您可能需要使用另一个查找界面来获取功能:

class IFeature
{
};

class IFeatureA : public IFeature
{
    virtual void DoSomething() = 0;
};

class IFeatureB : public IFeature
{
    virtual void DoSomethingElse() = 0;
};

class IFullComponent
{
    virtual void GetFeature( GUID featureId, IFeature** ppFeature ) = 0;
};

在这种情况下,您的要素界面必须从常见的东西中派生出来。

是的,此解决方案将使用多重继承(即您的具体组件将派生自IFeatureXX接口以及IFullComponent),但即使对于因感知到的额外复杂性而避免多重继承的人,请记住您只是继承接口,而不是实际实现。即使是在语言级别不支持多继承的java和C#也允许你这样做。

答案 2 :(得分:1)

假设您需要保留BaseInterfaceClass而不是使用它来更改类的继承(否则只需为每个功能定义一个接口并仅继承您需要的接口)。 您可以将多重继承与专用模板结合使用,以仅获取所需的接口部分:

template<bool B> class FeatureABase {};
template<> class FeatureABase<true> {
public:
  virtual void doFeatureA1() = 0;
  virtual void doFeatureA2() = 0;
};
//similar definitions for FeatureBBase and FutureCBase here
class BaseInterfaceClass: public FeatureABase<HAS_FEATURE_A>, public FeatureBBase<HAS_FEATURE_B>, public FeatureCBase<HAS_FEATURE_C>
{/*not flag dependent parts here*/};
//If you want to get rid of the preprocessor flags completely, you could define
//BaseInterfaceClass as a template itself:
template<bool HasA, bool HasB, bool HasC>
class BaseInterfaceClass: public FeatureABase<HasA>, public FeatureBBase<HasB>, public FeatureCBase<HasC>
{/*not flag dependent parts here*/};

如果您不想使用多重继承(因为您不想让对象变大),可以将其展开到一个继承链中:

template<bool B> class FeatureBBase: public FeatureABase<HAS_FEATURE_A> {};
template<> class FeatureBBase<true>: public FeatureABase<HAS_FEATURE_A> {
public:
    virtual void doFeatureB1() = 0;
    virtual void doFeatureB2() = 0;
    virtual void doFeatureB3() = 0;
};
template<bool B> class FeatureCBase: public FeatureBBase<HAS_FEATURE_B> {};
template<> class FeatureCBase<true>: public FeatureBBase<HAS_FEATURE_B> {
public:
    virtual void doFeatureC1() = 0;
}
class BaseInterfaceClass: public FeatureCBase<HAS_FEATURE_C>
{/*not flag dependent parts here*/};

这假设您的featureflags基本上是bool,因此如果未启用该功能,HAS_FEATURE_A0。如果不是这种情况,您可以将模板参数设为int,并专门针对1而不是true。如果未使用的功能标志未定义,则无法完全摆脱使用#if#ifdef,因为这是根据是否定义宏来做出决策的唯一方法。