在基类中创建一个知道派生类的方法是一个很好的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。
答案 0 :(得分:1)
你是对的,它的气味很糟糕,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();
请原谅任何语法错误我在这里没有验证正确性只是试图了解
答案 1 :(得分:1)
基于您最近澄清问题的替代解决方案。
我认为类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
答案 2 :(得分:0)
如果它也是A的正确上下文中的方法,但是在A中没有实现,则可以将其声明为abstract
方法,需要由任何派生类实现。 (或者你可以有一个只知道A的实现,但你可以在派生类中override
。)如果你觉得你有很多跨类知识,你可能没有足够的抽象。 / p>