5

最初我有一个设计问题,我需要一个超类的五个子类,除了两个之外,所有子类都使用相同的通用方法,另外两个类需要特殊处理。我想避免将方法写五次;两个特殊情况和三个相同的情况。

所以我让每个类都继承 SuperClass 及其 doSomething() 方法,并让 SubClassSpecial1 和 SubClassSpecial2 用他们自己的 doSomeThing 方法覆盖。

这一切都很好,直到我写了一个看起来像的方法

void fooBar(SuperClass obj) {
    obj.doSomething()
}

它可以称为fooBar( new SubClassSpecial1() );

问题是obj变量的运行时类现在是它的超类的,因此它将调用超类中定义的方法。当然,我可以在超类中创建一个抽象的 doSometing() 方法,并让每个子类实现自己的版本,但这会在三个类中重复代码。我想避免这样做......

如果我有很多分支,我将失去多态性所带来的任何收益

if(obj.getClass().getName() == "SubClassSpecial1" )  ((SubClassSpecial1) obj).doSomething()K;
else if ...

那么我应该怎么做才能使设计更加优雅和不老套呢?

4

3 回答 3

5

我对你的这部分问题有点困惑:

当然,我想做的是让每个对象调用它自己的 doSomething() 版本,但没有意识到为了做到这一点,需要将 obj 声明为子类方法之一。现在是一团糟。

当然声明无关紧要,doSomething() 方法总是会根据类的运行时类型被调用。

所以我认为你试图做的事情应该可以正常工作,例如这些声明都可以用来传递给 foobar 方法:

SuperClass sc1 = new SubClassSpecial1();
SubClassSpecial2 sc2 = new SubClassSpecial2();
//etc..
于 2009-11-02T14:42:21.407 回答
2

你所描述的应该可以正常工作。

obj.doSomething() 实际上是在调用超类的实现吗?如果是,则说明您没有正确覆盖它。检查您是否没有更改覆盖版本中的签名。

于 2009-11-02T14:50:36.217 回答
1

当你有这个:

void fooBar(SuperClass obj) {
    obj.doSomething();
}

那么编译时类型objSuperClass. 这意味着编译器将检查是否SuperClassdoSomething()方法。
运行时,您可以替换 的子类SuperClass,这就是Liskov 替换原则。方法foobar()不知道也不应该知道运行时类型obj是什么,只知道它派生自什么SuperClass,因此doSomething()可以调用。

至于你的例子:

fooBar( new SubClassSpecial1() );

在这种情况下,您碰巧知道参数的运行时类型是SubClassSpecial1专门覆盖doSomething(). 在所有情况下,都会调用正确的方法。

关于重构的一句话。您可能需要考虑重构您的层次结构。
您的基类SuperClass应定义doSomething()为抽象类。您的三个需要相同实现的类doSomething()应该从具有该特定实现的中间基类继承它。您的两个特殊类应该直接继承SuperClassdoSomething().

于 2009-11-03T23:24:05.347 回答