In a refactoring task, I have an old class, called OldClass
, that will be replaced with the newer better NewClass
.
OldClass
and NewClass
share have a lot in common, and for example share data members with the same name like memberA
, memberB
... But while they have the same name, these are members of OldClass
and NewClass
and are not shared through inheritance (nor do I want them to be shared).
They do have a common ancestor, so it would look like this:
┌──────────────┐
│CommonAncestor│
└───────▲──────┘
┌───────┴──────┐
│ ┌────┴────┐
│ │NewParent│
│ └────▲────┘
┌─────┴─────┐ ┌─────┴──────┐
│ OldClass │ │ NewClass │
├───────────┤ ├────────────┤
│int memberA│ │int memberA │
│int memberB│ │bool memberB│
└───────────┘ └────────────┘
My problem is that both will exist within the code for a while before the transition is fully over, with a setting at start enabling the switch between old and new mode.
That means that in a lot of place, I will have
CommonAncestor* c = ...;
if(newMode) {
NewClass* n = dynamic_cast<NewClass*>(c);
n->setMemberA(...);
n->setMemberB(...);
}
else {
OldClass* o = dynamic_cast<NewClass*>(c);
o->setMemberA(...);
o->setMemberB(...);
}
Which is awful (code duplication, and cast). (Of course, there is a lot more than two members...)
How can I avoid that? What would be a nice way for them to coexist without duplicating every member access and assignation?
The idea is that deleting OldClass
, later, should be as easy as possible.
Side note: In the case of instanciation, I avoided duplication by being able to construct an OldClass member from a NewClass one, so:
CommonAncestor* c;
NewClass* n;
n->setMemberA(...);
n->setMemberB(...);
if(newMode) {
c = n;
}
else {
OldClass* o(n); //we can make an Old by copying a New
c = o;
}
When you find yourself down-casting with dynamic_cast
, something has usually gone wrong in your design:
CommonAncestor* c = ...;
if(newMode) {
NewClass* n = dynamic_cast<NewClass*>(c);
n->setMemberA(...);
n->setMemberB(...);
}
This above example could instead be written as:
CommonAncestor* c = ...;
c->setMemberA(...);
c->setMemberB(...);
This would require setMemberA
and setMemberB
to be virtual
member functions, so calling the base class functions chooses an implementation from the derived class. Even if you weren't allowed to modify CommonAncestor
, you could:
class OldOrNewClass
containing virtual void setMemberA(...)
, et al.OldClass
inherit from OldOrNewClass
OldOrNewClass
can inherit from CommonAncestor
, so that OldClass
has only one parentNewClass
or NewParent
inherit from OldOrNewClass
CommonInterface*
to OldOrNewClass*
and then call the virtual member functionsCommonAncestor* c = ...;
OldOrNewClass* n = dynamic_cast<OldOrNewClass>(c);
n->setMemberA(...);
n->setMemberB(...);
Multiple inheritance is allowed in C++, so you can keep your old class hierarchy and add OldOrNewClass
to that. However, if you are allowed to modify CommonInterface
, you can save yourself this trouble.