7

Currently, I'm in a situation where I have some base class and many subclasses. The subclasses must override several virtual methods, however, sometimes the implementation for a virtual method in one subclass is exactly identical to another subclass. Should I just copy and paste the implementation code into the other subclass or is there a way to express an implementation for both subclasses at once?

The code below demonstrates my problem.

class A
{
    virtual void foo1() = 0;
    virtual void foo2() = 0;
};

class B : public A
{
    void foo1();
    void foo2();
};

class C : public A
{
    void foo1();
    void foo2();
};

class D : public A
{
    void foo1();
    void foo2();
};

void B::foo1()
{
     // Same implementation of foo1 as C
}

void C::foo1()
{
     // Same implementation of foo1 as B
}

void D::foo1()
{
     // Different implementation of foo1
}

void B::foo2()
{
     // Different implementation of foo2
}

void C::foo2()
{
     // Different implementation of foo2
}

void D::foo2()
{
     // Different implementation of foo2
}

Can I somehow combine implementation for B and C?

Note: I explicitly wrote out foo2, because otherwise subclass B and C would be identical in every way.

I'm wondering about the case when there are many (much larger than this toy example) subclasses and also they are inheriting many virtual functions with some virtual functions having the same implementation. In other words, a situation where creating a subclass every time a function coincidentally has the same implementation would clutter the whole inheritance structure.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
MathGeek
  • 123
  • 5
  • 1
    You can move the implementation to some other (possibly private) interface and use it inside those functions. Remember - *don't repeat yourself*. – Fureeish Jun 07 '19 at 20:31
  • `void B::A::foo1()` Huh? – Nikos C. Jun 07 '19 at 20:33
  • At this level there are a couple of solutions depending on how formal and wordy you want to be. But if `C` and `D` share a common implementation of `foo2` life gets ... exciting. – dmckee --- ex-moderator kitten Jun 07 '19 at 20:34
  • 1) You could provide a default implementation (in `A`, or in some layer between `A` and its children) and leave it up to classes to override the default impl only if they need different behavior. 2) You can create a separate set of free utility functions for the shared implementation, and delegate to those from `B` and `C`. 3) You can use `private` inheritance to share implementations. Just be aware of [the diamond problem](https://stackoverflow.com/q/2659116/3282436). – 0x5453 Jun 07 '19 at 20:35
  • @NikosC. Sorry about that. I fixed it – MathGeek Jun 07 '19 at 20:38
  • Alternatively, refactor your code to [prefer composition instead of inheritance](https://medium.com/humans-create-software/composition-over-inheritance-cb6f88070205). – 0x5453 Jun 07 '19 at 20:38
  • @0x5453 I just took a quick look into this but I'm not sure changing over to composition would make much of a difference because all of the sub-classes are already inheriting the all the functions. It just happens to be that some of the sub-classes have the same implementation – MathGeek Jun 07 '19 at 21:03

2 Answers2

12

Add an intermediate proxy class:

class BC : public A {
    void foo1();
};

class B : public BC {
    void foo2();
};

class C : public BC {
    void foo2();
};

As @Fureeish mentioned - don't repeat yourself.

What to do, if amount of classes / functions is massive:

Do the same. In the worst case, you'll end with (roughly) the same amount of code, with more classes, but fewer functions. Even then this is a net win, because you've removed a lot of code redundancy, and while inheritance structure is worse, the code quality is much better. In practice, such dense "collision matrix" suggests something is wrong with the classes themselves. Maybe those intermediate classes you up end creating should be written in the first place? In all cases, reducing code redundancy is a big win.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Radosław Cybulski
  • 2,952
  • 10
  • 21
  • 2
    Answer is fine, except that's not a proxy class. Just an intermediate common base for B and C. – Nikos C. Jun 07 '19 at 20:37
  • 1
    I was going to suggest some form of variation on the decorator pattern; assuming that the common functions are split randomly over the multiple subclasses... But, if it is as simple as just needing a _set_ of common functions across a group of subclasses - I think this works much better! – CurtisJC Jun 07 '19 at 20:39
4

I see two options:

  • Move the common code to some interface (possibly private) and simply call that interface inside your two functions.

  • Provide a default implementation and simply don't override it, i.e. instead of making foo1 pure virtual, just implement the default behaviour there.

Obviously it is possible that there is no default way of doing what foo1 should do. Then I suggest sticking with the first option.

Fureeish
  • 12,533
  • 4
  • 32
  • 62