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

时间:2012-05-21 21:16:34

标签: oop inheritance derived-class base-class

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

3 个答案:

答案 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>