1

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

4

3 回答 3

1

你是对的,在 A 中有一个需要了解子类中的实现的方法,它的味道很糟糕。也许您需要将此功能分解到另一个类中(而不是在继承层次结构中),并且在这种情况下更倾向于组合而不是继承。

在不了解您的代码的情况下确定要做什么有点困难。@Styxxy 提出了一个很好的观点,即使用抽象方法来解决这个问题,但这完全取决于你说“A 需要了解 B”时的意思。

如果一个对象被实例化为 a A,那么如何将其转换为 aB以获得您正在谈论的实现?它不能。

但是,如果一个对象被创建为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();

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

于 2012-05-21T21:40:49.260 回答
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:以便您可以在运行时确定如何根据 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
于 2012-05-21T23:50:34.773 回答
0

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

于 2012-05-21T21:27:02.637 回答