Search code examples
c++oopdesign-patternsinterfaceabstract-class

C++ refactor common method/logic without polluting interface


  • I have an interface:
class FooInterface {
  virtual void Method1(bool method) = 0;
};
  • I have multiple derived classes which implement this interface which all have common logic/method [Method1]:
class BarClass1 : FooInterface {
  void Method1(bool method) final {
    if (method) {
      Method2();
    } else {
      Method3();
    }
  }
    void Method2() {// BarClass1 logic};
    void Method3() {// BarClass1 logic};
};

class BarClass2 : FooInterface {
  void Method1(bool method) final {
    if (method) {
      Method2();
    } else {
      Method3();
    }
  }
  void Method2() {// BarClass2 logic};
  void Method3() {// BarClass2 logic};
};
  • I want to refactor out the common logic without polluting my interface.

Does it make sense to move the common logic into a AbstractClass or is there a more optimal design pattern?

class FooAbstractClass : FooInterface {
  void Method1(bool method) final {
    if (method) {
      Method2();
    } else {
      Method3();
    }
  }

  virtual void Method2() = 0;
  virtual void Method3() = 0;
};

class BarClass1 : FooAbstractClass {
    void Method2() final {// BarClass1 logic};
    void Method3() final {// BarClass1 logic};
};

class BarClass2 : FooAbstractClass {
    void Method2() final {// BarClass2 logic};
    void Method3() final {// BarClass2 logic};
};

Intended usage:

BarClass1 bar1;
bar1.Method1(true);

BarClass2 bar2;
bar2.Method1(false);

Solution

  • A possible workaround is to have another interface class, for the Method2 and Method3 functions. Then create a BarBase class which inherits both interfaces, but also defines (implements) the Method1 from FooInterface.

    Then the two BarClass1 and BarClass2 classes inherits BarBase and implements Method2 and Method3 each in their own way.

    Perhaps something like this:

    struct BarInterface
    {
        virtual void Method2() = 0;
        virtual void Method3() = 0;
    };
    
    struct BarBase : public FooInterface, public BarInterface
    {
        void Method1() override final { /* Call Method2 or Method3... */ }
    };
    
    class BarClass1 final : public BarBase
    {
    public:
        void Method2() override { /* ... */ }
        void Method2() override { /* ... */ }
    };
    
    class BarClass2 final : public BarBase
    {
    public:
        void Method2() override { /* ... */ }
        void Method2() override { /* ... */ }
    };
    

    This way you can still use pointers to (for example) a BarClass1 object when passing it to functions wanting a pointer to a FooInterface.

    As an alternative, if you don't need a separate BarInterface you can always declare those functions in the BarBase class instead.