1

该项目有一个目标:使用以界面为中心的设计。基本上,我们声明只有公共纯函数的类,并让真正的类继承自它们。

现在出现了一个问题——如何重用效用函数?

这是一个例子:

class InterF1 {
  public:
    int f1() = 0;
    int g1() = 0;   
}; 

class InterF2 {
  public:
    int f2() = 0;
}; 

class C1: public InterF1 {
  public:
    int f1();
    int g1();
};


class C2: public InterF1, InterF2 {
  public:
    int f1();
    int g1();
    int f2();
};

在实现 C1::f1() 和 C2::f1() 时,我看到这两个函数之间的代码重复。我应该如何删除重复?

以下是我的想法:

1)我可以像这样向接口类添加保护:

class InterF1 {
  public:
    int f1() = 0;
    int g1() = 0;   
  protected:
    int util();
    int convenient_var_calculated_by_util;
}; 

在上述设计中,C1::f1() 和 C2::f2() 都可以调用 util()。

2)我可以抽象一个新接口:

class UtilI {
    public:
      int util(int in_var) = 0;
};

在本设计中,InterF1、C1 和 C2 更改为

class InterF1 {
  public:
    int f1(UtilI u) = 0;
    int g1() = 0;   
}; 

class C1: public InterF1 {
  public:
    int f1(UtilI u);
    int g1();
};


class C2: public InterF1, InterF2 {
  public:
    int f1(UtilI u);
    int g1();
    int f2();
};

C1::f1() 和 C2::f1() 都调用 UtilI API。

解决方案 2 似乎更符合我们的“以界面为中心”的目标,而且看起来确实更好。但我有一个担心,InterF1 和 InterF2 本身应该是比 UtilI 更高级别的抽象,它更多的是实现细节,我是否混合了 2 个级别?换句话说,如果稍后实现更改,我将需要再次更新 f1() 和 f2() 签名,这听起来不对。

解决方案 1 看起来确实很方便,但对我来说它看起来不那么“纯粹”,我在这里是不是太教条了?

4

2 回答 2

3

通常我会说你应该把通用实现放在基类中——但是你要求基类是纯虚拟的。因此,我建议:

  1. 创建中间实现类

    如果每个想要实现的类InterF2也想要实现,这种方法是可以的InterF1

    方法是实现

    class InterF1Impl: public InterF1 {
      public:
        int f1();
        int g1();
    }
    

    thenC1C2都可以派生自InterF1Impl其中实现了 commonf1()g1()方法。

    但是,如果您有另一个类C3 : public InterF2想要共享f2()withC2但不想实现的实现,则此方法将无法很好地扩展InterF1

    另一种(更好的)方法是

  2. 使用组合来包含一个实现类

    在这种方法中,InterF1Impl: public InterF1如上所述实现,但不是派生 C1: public InterF1Impl,而是让InterF1Impl成为C1类的一部分。

    class C1: public InterF1 {
      private:
        InterF1Impl * f1impl;
      public:
        int f1();
        int g1();
    }
    

    C1::f1()andC1::g1()方法只是从 中调用相应的方法f1impl

    这样,如果你需要一个通用的实现f2(),那么你也可以实现InterF2: public InterF2,并且C2可以以类似的方式实现:

    class C2: public InterF1, InterF2 {
      private:
        InterF1Impl * f1impl;
        InterF2Impl * f2impl;
      public:
        int f1(); /* calls f1impl->f1() */
        int g1(); /* calls f1impl->g1() */
        int f2(); /* calls f2impl->f2() */
    }
    

    And another class could just use the implementation of InterF2 without also implementing InterF1 if you later needed such a class.

于 2012-04-10T03:11:51.123 回答
0

你的第二个解决方案比第一个更好,但我会做不同的事情。

首先,代码重用可以通过对象之间的继承或组合关系来实现。在这种情况下,组合方法更有意义,如下所述。

您的类的用户可能不关心util()方法的实现,甚至不关心它的存在。因此,拥有UtilI接口并将其作为公共 API 的一部分似乎是不必要的。由于实用程序逻辑是您的实现的一部分,而不是接口的一部分,因此此类应仅用于 C1 和 C2 类的实现。如果你这样做,你可能不需要UtilI接口,并且可以只替换为具体类Util。您可能可以使此类无状态,因此它只有静态方法。如果不是,那么它可能是单例,或者您的类可以创建自己的实例(取决于您的需要)。

以界面为中心的设计是一个很好的目标,但重要的是你只公开应该公开的东西。这是设计任何 API 的重要部分,它应该是完整的和最小的

于 2012-04-10T02:58:34.930 回答