Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Override method for a family of subclasses

Tags:

c++

overriding

Given legacy code, the system has the following hierarchy of classes:

          Base
          ^
          |
----------+---------------
^      ^     ^     ^     ^ 
|      |     |     |     |
A1     B1    C1    D1    E1
^      ^     ^     ^     ^ 
|      |     |     |     |
A2     B2    C2    D2    E2
.......
^      ^     ^     ^     ^ 
|      |     |     |     |
An     Bn    Cn    Dn    En

The hierarchy represents messages in some specific domain.

Base class is of course base class of all messages. A1..E1 are messages belonging to version 1 of the domain, A2..E2 to version 2, and so on. Please note that An must inherit directly from An-1, since An overrides specific methods of An-1.

There's some functionality that is common to all messages, so it's defined as Base::PerformFunctionality. Some part of the functionality is specific to version n only, so there's virtual function Base::SpecificPartOfFunctionality, which is called by Base::PerformFunctionality.

So my problem is how to override Base::SpecificPartOfFunctionality by all An..En.

I see 2 possible solutions, which I don't like too much:

  1. Implement Base::SpecificPartOfFunctionality in each and each An..En.

    The problem with this solution is that the implementation should be exactly same for each class, so I just repeat the code.

    Additional problem is that if a new class Fn is introduced, developer may forget to implement SpecificPartOfFunctionality.

  2. Introduce BaseN class deriving from Base, while each An..En derive from BaseN too:

    class BaseN : public Base {
       //...
       SpecificPartOfFunctionality() { ...}
    };
    
    class An: public An-1, public BaseN { .. }
    

    The problem with this solution is that it introduces diamond problem.

    Additional problem is what will happen if some other version m needs to override Base::SpecificPartOfFunctionality too. Following the solution we'll introduce BaseM, which will override Base::SpecificPartOfFunctionality. So which SpecificPartOfFunctionality will be called for An - of BaseN or of BaseN. It's complete mess.

Any suggestions?

like image 564
dimba Avatar asked Oct 12 '22 01:10

dimba


2 Answers

struct Base {
  virtual void PerformFunctionality() {
    stuff();
    SpecificPartOfFunctionality();
    more_stuff();
  }
private:
  virtual void SpecificPartOfFunctionality() {}
};

template<class Prev>
struct Version3 : Prev {
protected:  // Not private if v4's override might need to call this.
  virtual void SpecificPartOfFunctionality() {
    v3_specific_stuff();
  }
};

struct A1 : Base {};
struct A2 : A1 {};
struct A3 : Version3<A2> {};

struct B1 : Base {};
struct B2 : B1 {};
struct B3 : Version3<B2> {};

The only downside is you can't easily forward constructors in current C++, and are probably going to always use the default constructor for A2 and B2 just for simplicity.

In C++0x, however, you can forward constructors:

template<class Prev>
struct Version3 : Prev {
  using Prev::Prev;
  //...
};

struct C2 : C1 {
  C2(int);  // No default constructor.
};
struct C3 : Version3<C2> {
  C3() : Version3<C2>(42) {}
};

Please note that An must inherit directly from An-1, since An overrides specific methods of An-1.

You have misunderstood something. An does not need to inherit directly from An-1. For example, this works just fine:

struct Example {
  virtual void f();
};

template<class B>
struct Intermediate : B {};

struct Concrete : Intermediate<Example> {
  virtual void f();  // Overrides Example::f.
};

Remember that if this didn't work, then your An couldn't override SpecificPartOfFunctionality from your Base class in the first place! An doesn't directly inherit from Base in any of your examples.

like image 136
Fred Nurk Avatar answered Oct 17 '22 05:10

Fred Nurk


Define a friend function at global scope which implements the functionality, and have SpecificPartsOfFunctionality do nothing more than call it. E.g.

void FunctionalityN(Base* b)
{
    b->DoThings();
    ...
}

class An : public Base
{
    friend void FunctionalityN();
    virtual void SpecificPartOfFunctionality() { FunctionalityN(this); }
    ....
};

Strictly speaking FunctionalityN wouldn't even have to be a friend, though it would make things easier. This makes extending to Fn, Gn, etc. straightforward and subsequently replacing FunctionalityN with NewFunctionalityN pretty easy, too.

like image 37
Matt Phillips Avatar answered Oct 17 '22 05:10

Matt Phillips