【问题标题】:Should a base class contain a method that refers to a derived class?基类是否应该包含引用派生类的方法?
【发布时间】:2012-05-28 09:29:11
【问题描述】:

在知道派生类的基类中创建方法是一种好的 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。

【问题讨论】:

  • 你说“所有的实现都是一样的”,这让我相信A 知道为了计算distance_to_simple_coord_system() 的值它需要知道的一切。您最初声明A 需要了解B 中的实现,但在您澄清后似乎并非如此。我怀疑,您的子类对distance_to_simple_coordinate_system () 的实现会略有不同,否则为什么要覆盖此方法?
  • distance_to_simple_coord_system 是 B 类的方法,只有这个类知道如何计算。 A 类和所有其他类需要知道 B 类存在,因此使用distance_to_complex_coordinate_system
  • 我不想重写distance_to_simple_coordinate_system这个方法只存在于B类中,其他地方都不存在。问题在于 distance_to_complex_coordinate_system 需要知道 B 类存在。

标签: oop inheritance derived-class base-class


【解决方案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 {}

[编辑]

关注你的 cmets...

我同意你的看法。 感觉不正确的是,您的层次结构的 all 依赖于类 B,而不是父类。这对于 OO 设计来说是根本错误的,您需要通过将其移至 A 或另一个独立的帮助器类来从此类中删除 共享功能

注意:有时 OO 设计并不能很好地适应现实世界中的具体继承层次结构,这与 10 年前的教科书会让我们相信的相反。

此解决方案允许您为必要的位保留 A、B、C、D 层次结构,并且我使用一个接口来打破B 中的共享功能。 p>

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:以便您可以在运行时确定如何根据 A、B、C 或 D 类进行计算的实现

考虑到C 可能需要不同的方法来计算它的距离到复杂坐标系,你可以这样做...

   /*
    * 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

【讨论】:

  • 非常感谢您的帮助。是的,我可以从层次结构中删除 B,但是我必须在 B 中重新实现 A 和 B 之间的通用功能。B 也应该能够调用distance_to_complex_coordinate_system。根据您的建议,我认为可能我可以创建另一个类 AB,它是 A 和 B 的父类(B 不扩展 A),其中包含两者之间的通用功能。然后分别为 A 和 B 我可以在他们每个人中实现distance_to_complex_coordinate_system
  • 我仍然有一种感觉,虽然这在某种程度上是一种人工/技术解决方案,而且不够优雅。从概念上讲,我需要在两个地方(在 A 和 B)定义 distance_to_complex_coordinate_system
  • 我认为您编辑后的设计方向是正确的。我将尝试实现它,看看它是如何工作的。谢谢你的帮助。
【解决方案2】:

你是对的,在 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();

请原谅任何语法错误,我在这里没有验证正确性,只是想表达一个观点

【讨论】:

  • 布拉德和@Styxxy 感谢您的快速回答。我想我理解你的建议(如果不是请纠正我)并且完全同意你的看法。但是我主要担心的是该方法并不是真正抽象的,它只是一个始终相同的方法,只需要知道 B 类的存在。我已经编辑了这个问题以使这一点更清晰。
【解决方案3】:

如果它也是 A 的正确上下文中的方法,但不能在 A 中实现,则可以将其声明为 abstract 方法,该方法需要由任何派生类实现。 (或者你可以有一个只知道 A 的实现,但你可以在派生类中 override。)如果你觉得你有很多跨类知识,那么你可能没有足够抽象。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-10-17
    • 2011-05-11
    • 2021-06-17
    • 1970-01-01
    • 1970-01-01
    • 2011-06-01
    • 2013-04-10
    • 1970-01-01
    相关资源
    最近更新 更多