mercredi 6 mai 2020

Is there a way to use "sister" function implementation in a class derived from an abstract class?

I have a design issue. I'd like to use a function implementation from a "sister" class when deriving from an abstract class if it is possible.

Problem

I have some "historic" code that adds a behavior to a base class using inheritance.

Old code

///////////////// Historic code ////////////////:

class IBase
{
    int value;
    virtual int method(){
        return value;
    };
}

class DerivedHist : IBase {
    void otherMethod(){
        return;
    }
}

New code

After some modifications, I ended up specializing the IBase in 2 derived classes (Derived1 and Derived2), and changed the IBase class to an abstract class. The problem is that I'd like to add the DerivedHist class behavior to both classes, and I don't see how to do it in a good way.

///////////////////// New code //////////////////////////:

//this time IBase is an abstract class
class IBase
{
    int value;
    virtual int method() = 0;
}

class DerivedHist : IBase {
    void otherMethod(){
        return;
    }

    //I'd like to use Derived1's or Derived2's implementation
    virtual int method(){
        //???
    }
}

class Derived1 : IBase {
    virtual int method(){
        return 2*value;
    }
}

class Derived2 : IBase {
    virtual int method(){
        return 3*value;
    }
}

I don't see exactly how to put back the DerivedHist class in place...

Solutions

I came up with some ideas:

  1. Write 2 classes equivalent to DerivedHist that inherit from Derived1 (say Derived1Hist) and from Derived2 (say Derived2Hist), but that means having two classes with almost the same code.

                                    IBase
                                    /   \
                            Derived1    Derived2
                            /               \
                        Derived1Hist        Derived2Hist
    
  2. Using something like this in the DerivedHist constructor:

    DerivedHist(IBase* other) : IBase(*other){
        ...
    }
    

    and call it with a dynamic cast:

    Derived1 derived1(...);
    DerivedHist derivedHist(dynamic_cast<IBase*>(derived1));
    

    Wouldn't this use the correct implementation of method() ? As we can call dynamic_cast(new Derived1(...))->method() I imagined that passing something like this in a copy constructor could work. I couldn't find a way to have the code compiling with something like this...

  3. Have one of the Derived[1,2] object as a member

    class DerivedHist : IBase {
        IBase methodHolder;
    
        DerivedHist(IBase other) : methodHolder(other){
            ...
        }
    
        void otherMethod(){
            return;
        }
    
        virtual int method(){
            //here I'd have to copy the relevant data members to the other object
            methodHolder.value = value;
            //and then call the other method
            return methodHolder.method();
        }
    }
    

    This looks like the best solution of the 3.

    But here the problem that I see is that I have not the data synced from DerivedHist and its methodHolder member.

    I could keep track of every changes and apply it to methodHolder but this does not seem to be the perfect solution.

Summary

To summarize, the two main questions I have are the following:

  1. Is there a way to call an implementation of method() from another derived class ?
  2. Otherwise, what kind of pattern should I use to solve this issue ?

Aucun commentaire:

Enregistrer un commentaire