英文:
Replacing a class without deleting the old one, avoiding access duplication
问题
在重构任务中,我有一个名为 OldClass
的旧类,它将被更好的新类 NewClass
替代。
OldClass
和 NewClass
有很多共同之处,例如共享具有相同名称的数据成员,如 memberA
、memberB
... 但虽然它们具有相同的名称,这些成员属于 OldClass
和 NewClass
,并且不通过继承共享(我也不希望它们共享)。
它们确实有一个共同的祖先,看起来像这样:
┌──────────────┐
│CommonAncestor│
└───────▲──────┘
┌───────┴──────┐
│ ┌────┴────┐
│ │NewParent│
│ └────▲────┘
┌─────┴─────┐ ┌─────┴──────┐
│ OldClass │ │ NewClass │
├───────────┤ ├────────────┤
│int memberA│ │int memberA │
│int memberB│ │bool memberB│
└───────────┘ └────────────┘
我的问题是,在完全过渡之前,这两者都会在代码中存在一段时间,开始时会启用旧模式和新模式之间的切换。
这意味着在许多地方,我将会有以下情况:
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(...);
}
这很糟糕(代码重复和类型转换)。当然,不仅有两个成员...
我如何避免这种情况?有没有一种不用复制每个成员访问和赋值就能让它们共存的好方法?
想法是尽可能轻松地删除 OldClass
。___
附注:在实例化的情况下,我通过能够从 NewClass
中构建一个 OldClass
成员来避免复制,所以:
CommonAncestor* c;
NewClass* n;
n->setMemberA(...);
n->setMemberB(...);
if(newMode) {
c = n;
}
else {
OldClass* o(n); //我们可以通过复制一个新对象来创建一个旧对象
c = o;
}
英文:
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;
}
答案1
得分: 2
当您使用dynamic_cast
进行向下转型时,通常意味着在设计中出现了问题:
CommonAncestor* c = ...;
if(newMode) {
NewClass* n = dynamic_cast<NewClass*>(c);
n->setMemberA(...);
n->setMemberB(...);
}
上述示例可以改写为:
CommonAncestor* c = ...;
c->setMemberA(...);
c->setMemberB(...);
这需要使setMemberA
和setMemberB
成为虚成员函数,以便调用基类函数会选择派生类的实现。即使您无法修改CommonAncestor
,您仍然可以:
- 创建一个新的
OldOrNewClass
类,其中包含virtual void setMemberA(...)
等成员函数。 - 使
OldClass
继承自OldOrNewClass
。 OldOrNewClass
可以继承自CommonAncestor
,以使OldClass
只有一个父类。- 通过将
CommonInterface*
向下转型为OldOrNewClass*
,然后调用虚成员函数:
CommonAncestor* c = ...;
OldOrNewClass* n = dynamic_cast<OldOrNewClass*>(c);
n->setMemberA(...);
n->setMemberB(...);
在C++中允许多重继承,因此您可以保留旧类层次结构并将OldOrNewClass
添加到其中。但是,如果允许修改CommonInterface
,则可以避免这种麻烦。
还请参阅C++中为什么需要虚函数?。
英文:
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:
- create a new
class OldOrNewClass
containingvirtual void setMemberA(...)
, et al. - make
OldClass
inherit fromOldOrNewClass
OldOrNewClass
can inherit fromCommonAncestor
, so thatOldClass
has only one parent
- make
NewClass
orNewParent
inherit fromOldOrNewClass
- downcast your
CommonInterface*
toOldOrNewClass*
and then call the virtual member functions
CommonAncestor* 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.
答案2
得分: 1
以下是翻译好的部分:
模板用于代码去重:
template <typename T, typename F>
void manipulateClass(CommonAncestor* c, F&& functor) { // 请给我一个更好的名字
if (T* n = dynamic_cast<T*>(c)) {
std::forward<Functor>(functor)(n);
} else {
throw std::invalid_argument("Class has incorrect type for selected mode");
}
}
...
auto const setter = [&](auto n) { // 所有变量都被捕获
n->setMember(...);
n->setMember(...);
// ...
};
if (newMode)
manipulateClass<NewClass>(c, setter);
else
manipulateClass<OldClass>(c, setter);
类似的方法也可以用于构造。此外,我建议使用std::unique_ptr<Class>
或std::shared_ptr<Class>
而不是Class*
。
英文:
Templates are for code deduplication:
template <typename T, typename F>
void manipulateClass(CommonAncestor* c, F&& functor) { // please give me a better name
if (T* n = dynamic_cast<T*>(c)) {
std::forward<Functor>(functor)(n);
} else {
throw std::invalid_argument("Class has incorrect type for selected mode");
}
}
...
auto const setter = [&](auto n) { // all variables captured
n->setMember(...);
n->setMember(...);
// ...
};
if (newMode)
manipulateClass<NewClass>(c,setter);
else
manipulateClass<OldClass>(c,setter);
A similar thing can be done for construction. Also, I would recommend using either std::unique_ptr<Class>
or std::shared_ptr<Class>
instead of Class*
.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论