基类是否应包含引用派生类的方法



在基类中创建了解派生类的方法是否是一个很好的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。如果你觉得你有很多跨阶级的知识,你可能没有足够抽象。

最新更新