在基类中创建了解派生类的方法是否是一个很好的OO编程实践?
例如,考虑类 A、B、C、D,其中类 B 继承自 A,类 C 和 D 继承自 B.现在考虑一个所有从 A 继承的类(包括 A 本身(通用的方法,该方法需要了解类 B 才能工作(例如:类 A/B/C/或 D 的对象与对象 B 的相对距离(。由于此方法对于所有类(A,B,C,D(都是通用的,因此将其放在A类中听起来合乎逻辑。但是,在这种情况下,我们将在基类中有一个"知道"派生类的方法。
例:
Class A {
# simple coordinate system
distance_to_complex_coordinate_system (B complexCoord) {
return complexCoord.distance_to_simple_coord_system(self)
}
}
Class B extends A {
# complex coordinate system
distance_to_simple_coordinate_system (simpleCoord) {
# do calculations
}
}
Class C extends A {}
Class D extends A {}
...
在上面的示例中,distance_to_complex_coordinate_system
是所有简单坐标系(即 A、B、C 和 D(都应该具有的方法,并且所有坐标系的实现完全相同。它不是一个抽象类,因此不应被覆盖。
这在OO的角度来看是否有意义,如果有意义,它真的是一个很好的编程实践吗?如果没有,任何关于如何避免这种情况的建议将不胜感激。
在我看来,这不是一个好的做法,但我找不到一种很好的方法来避免它而不会不必要地重复代码。顺便说一句,虽然可能并不重要 - 我正在使用Perl。
基于您最近对问题的澄清的替代解决方案。
我认为类B
中的功能应该分解为一个辅助类(组合(。您已经说过B
对于知道如何计算distance_to_simple_coord_system((是不可或缺的。您可以从类层次结构中删除B
吗?
像这样的东西,其中 A 拥有 B 的一个实例,但它不再是子类。如何实际创建和设置 B 的实例取决于您。
class A {
B b = new B();
distance_to_complex_coordinate_system() {
// i'm guessing that passing "this" maybe of use to you
b.distance_to_simple_coord_system(this);
}
}
class B { // does not extend A
Public distance_to_simple_coord_system(A a) {}
}
class C extends A {}
class D extends A {}
[编辑]
关注您的评论...
我同意你的看法。这里感觉不到的是所有层次结构都依赖于类B
,而不是父类。这对于 OO 设计来说是根本错误的,您需要通过将共享功能移动到 A
或另一个独立的帮助程序类中来将其从此类中删除。
注意:有时OO设计不适合现实世界中具体的继承层次结构,这与10年前的教科书所相信的相反。
此解决方案允许您保留必要的位的 A、B、C、D 层次结构,我使用接口将共享功能与B
区分开来。
class A {
Calc calc = new SimpleCalc();
distance_to_complex_coordinate_system() {
getCalc().distance_to_simple_coord_system(this);
}
Calc getCalc() {
return this.calc;
}
}
class B extends A {}
class C extends A {}
class D extends A {}
interface Calc {
distance_to_simple_coord_system(A a);
}
class SimpleCalc implements Calc {
distance_to_simple_coord_system(A a) {
// implementation assuming you don't need an "instanceof B" passed in
}
}
问:为什么要使用接口?
答:这样您就可以在运行时确定如何基于类 A、B、C 或 D 进行计算的实现
。考虑到C
可能需要一种不同的方法来计算其distance_to_complex_coordinate_system,你可以这样做......
/*
* Calculator specific to C
*/
class CalcForC implements Calc {
distance_to_simple_coord_system(C c) {
// implementation specific to "C"
}
}
class C extends A {
Calc calcForC = new Calc();
/* override */
Calc getCalc() {
return this.calcForC;
}
}
C c = new C();
c.distance_to_complex_coordinate_system(); // defined on A but calls getCalc() on C
对的,在 A 中有一个需要了解子类中实现的方法,它闻起来很糟糕。也许您需要将此功能分解到另一个类(不在继承层次结构中(,并且在这种情况下更喜欢组合而不是继承。
如果不了解您的代码,就很难确定该怎么做。 @Styxxy提出了一个关于使用抽象方法作为解决这个问题的好观点,但这完全取决于你说"A 需要了解 B"时的意思。
如果一个对象被实例化为A
,那么如何将其转换为B
以获得您正在谈论的实现?不能。
但是,如果对象被创建为B
则可以调用一个通用方法A
该方法又调用将被其子类覆盖的抽象方法(例如 B
(。松散地说,这就是Template
设计模式的作用。父类A
定义了一些要执行的步骤以及这些步骤的顺序,但它将实现推迟到子类。
abstract class A {
public void doStuff() {
doThis();
doThat();
}
abstract void doThis();
abstract void doThat();
}
class B extends A {
@Override
void doThis() {
// implementation
}
@Override
void doThat() {
// implementation
}
}
// from somewhere in code...your subclass B knows doThis() will be called before doThat()
B b = new B();
b.doStuff();
请原谅任何语法错误,我在这里没有验证正确性,只是想表达一个观点
如果它也是在 A 的正确上下文中的方法,但是不能在 A 中实现,则可以将其声明为需要由任何派生类实现的abstract
方法。 (或者,您可以有一个只知道 A 的实现,但您可以在派生类中override
。如果你觉得你有很多跨阶级的知识,你可能没有足够抽象。