8

I have got myself into a strange issue now. Ill write a really simplified version of the same.

class Base
{
public:
  virtual int func1()=0;
  virtual int func2()=0;
protected:
  int n;
};

class der1: public Base
{
  // implements the virtual functions of the base and uses the protected data 
  // members of the base.
};

class der2: public Base
{
  // implements the virtual functions of the base and uses the protected data 
  // members of the base.
}

Now the problem.... both der1 and der2 implements the virtual functions of base pretty much the same way. But some other classes (der3, der4) has their own implementations. But still need to inherit from base. How do i refactor the code to remove the code duplication in an oop manner?

Christophe
  • 68,716
  • 7
  • 72
  • 138
lifeOfPi
  • 178
  • 1
  • 12
  • You can provide commonly used code in the base class as protected member functions. – πάντα ῥεῖ Oct 21 '18 at 09:26
  • 4
    You can derive `Der1` and `Der2` from `Base12` that itself derives from `Base`. – Evg Oct 21 '18 at 09:27
  • as I said, only two classes have the code in common and again some other classes have a different set of common code. So, there is no common code that caters all these 4 classes. Similarly, in future there might be some more classes that inherit this base class. Hence, don't really want to bother the base class. – lifeOfPi Oct 21 '18 at 09:28
  • _@lifeOfPi_ Then probably @Evg's proposal is the way to go. – πάντα ῥεῖ Oct 21 '18 at 09:29
  • i tried @Evg's solution and faced the problem that Base12 has to now implement these pure virtual functions of Base and again, der1 and der2 also has to implement the same ... is there a better way? – lifeOfPi Oct 21 '18 at 09:32
  • @lifeOfPi With `Base12` you should not implement the pure virtual functions, but do what I said in the beginning. – πάντα ῥεῖ Oct 21 '18 at 09:33
  • 1
    You can keep `Base12` abstract as well, there shouldn't be need to instantiate those... Anyway, I'd be more inclined to πάντα ῥεῖ solution - If there's something common, define it in `Base`, then override this implementation in `Der3` and `Der4`. No need for yet another layer of abstraction. – Yksisarvinen Oct 21 '18 at 09:34
  • @πάνταῥεῖ the problem is if I derive from base, ill have to provide a definition of those pure virtual functions, else I can't create a concrete class. Do you want to say that let this intermediate class be an abstract and not a concrete? – lifeOfPi Oct 21 '18 at 09:37
  • @lifeOfPi _" if I derive from base, ill have to provide a definition of those pure virtual functions"_ that's wrong. You can leave any intermediate layer of another base class abstract, unless you need to instantiate it directly. – πάντα ῥεῖ Oct 21 '18 at 09:38
  • @πάνταῥεῖ: do you mind sharing a simple code snippet where the intermediate class has the common code for der1 and der2.... and yet being an abstract class? – lifeOfPi Oct 21 '18 at 09:40
  • _@lifeOfPi_ I did now. But IMO @Yksisarvinen is right. If you have commonly used stuff you should place it in the original base class. This makes extending the class hierarchy easier later on. – πάντα ῥεῖ Oct 21 '18 at 09:49
  • This whole question depends entirely on what "pretty much the same way" means, exactly. You should have provided a concrete example. – Christian Hackl Oct 21 '18 at 10:48

3 Answers3

5

Here's one solution using an intermediate layer of another abstract base class:

class Base12 : public Base {
protected: 
    int commonFuncStuffA() {
       // Commonly used stuff 
    }

    int commonFuncStuffB() {
    }
};

class der1: public Base12
{
public:
    virtual int func1() {
        n = commonFuncStuffA();
    }
    virtual int func2() {
        n = somethingElse;
    }
};

class der2: public Base12
{
public:
    virtual int func1() {
        n = commonFuncStuffA();
    }
    virtual int func2() {
        n = commonFuncStuffB();
    }
};

What I'd do for real production code design looks a bit different though.

  1. Declare an interface for the pure virtual functions

    struct IMyInterface {
        virtual int func1() = 0;
        virtual int func2() = 0;
        virtual ~IMyInterface {}
    };
    
  2. Provide a abstract base class with the commonly used data members and functions

    class BaseImpl : public IMyInterface {
    protected: 
        int n;
        int commonFuncStuffA() {
            // Commonly used stuff 
        }
    
        int commonFuncStuffB() {
            // Commonly used stuff 
        }
    };
    
  3. Provide implementations of the interface in the finally derived classes

    class der1: public BaseImpl {
    public:
        virtual int func1() {
            n = commonFuncStuffA();
        }
        virtual int func2() {
            n = somethingElse;
        }
    };
    
    
    class der2: public BaseImpl {
    public:
        virtual int func1() {
            n = commonFuncStuffA();
        }
        virtual int func2() {
            n = commonFuncStuffB();
        }
    };
    
    class der3: public IMyInterface {
    public:
        virtual int func1() {
            // Some completely different implementation of the interface
        }
        virtual int func2() {
            // Some completely different implementation of the interface
        }
    };
    
    class der4: public IMyInterface {
    public:
        virtual int func1() {
            // Some completely different implementation of the interface
        }
        virtual int func2() {
            // Some completely different implementation of the interface
        }
    };
    
πάντα ῥεῖ
  • 1
  • 13
  • 116
  • 190
3

Option 1

You may consider using your most common implementation in the Base class. The main feature or drawback of this method is that the base class would no longer be abstract. If this is a problem, go to option 2.

Maybe you can even cope with differences in the derived classes by using the template method pattern to extract the differences in protected virtual functions invoked by the template method.

In anyway, for derived classes that need a completely different appoach, you'd just override the the Base class' method.

class Base
{
public:
  virtual int func1();
  virtual int func2()=0;
protected:
  virtual void f1_specific_part1()=0;
  virtual void f1_specific_part2()=0; 
  int n;
};
int Base::func1() { // common skeleton of the algorithm
     ...  
     f1_specific_part1(); 
     ... 
     f1_specific_part2(); 
     ...
}     

class Der1: public Base
{ 
protected:
 void f1_specific_part1() override;  // Implements the specific variation
 virtual void f1_specific_part2() override;       
};

Option 2

You may consider to factorize the common code of the derived classes into a protected method of the Base class.

The override of the pure virtual function would then just call the base class protected common function (for der1 and der2) or just use their own implementation that is completely different (for der3 and der4).

class Base
{
public:
  virtual int func1()=0;
  virtual int func2()=0;
protected:
  int common_part1_funct1(); // implements some common parts 
  int common_part2_funct1();
  int n;
};

class Der1: public Base
{
...
  int func1() override { 
     common_part1_funct1();
     ...
     common_part2_funct1(); 
     ... 
  }
};

Option 3 ?

Important remark: My answer assumes that there are many commonalities between most of the derived classes. However if you have only a small subset of derived classes that share some commonalities, then the answer of Evg would be would be more appropriate.

Christophe
  • 68,716
  • 7
  • 72
  • 138
1

The idea with Base12 is:

struct Base {
    virtual int func1() = 0;
    virtual int func2() = 0;
};

struct Base12 : Base {
protected:
    int func12();
};

struct Der1: public Base12 {
    virtual int func1() {
        return func12();
    virtual int func2() {
        return func12();
};

struct Der2: public Base12 {
    virtual int func1() {
        return func12();
    virtual int func2() {
        return func12();
};
Evg
  • 25,259
  • 5
  • 41
  • 83