【问题标题】:Polymorphism best practice多态性最佳实践
【发布时间】:2019-02-04 17:57:03
【问题描述】:

我一直在研究 java 设计模式以使我的项目更有条理,我需要知道的一件事是,如果以下方法是好的做法。

当使用父抽象类或接口时,子类的方法在父类中定义。

我的问题是,当我有一堆子类时,如果每个子类都覆盖父类中的方法 A,但其中一个子类需要有不同的方法 B,那么在父类中定义方法 B 是否是一种好习惯,即使它仅用于该单个子类。

这带来了一个问题,即 methodB 将需要在每个子类中使用,即使它从未从该类中调用。

我打算这样做吗?好像不太对。

代码示例:

public interface Parent{

  public void methodA();

  //Should this method be in the parent as it is only used once?
  public void methodB();

}

子类:

public class First implements Parent{

  @Override
  public void methodA(){
    System.out.println("This is method A");
  }

  // Does Nothing but java must have it used in the sub class
  @Override
  public void methodB(){
    throw new UnsupportedOperationException("Do not call methodB from class First!!");
  }
}



public class Second implements Parent{

@Override
  public void methodA(){
    System.out.println("This is method A");
  }

  // methodB is being used for this sub-class
  @Override
  public void methodB(){
    System.out.println("This is method B");  
  }

}

从上面的代码可以看出,我只对第二个子类使用了methodB,但是还是要求在第一个子类中使用,否则会抛出异常。由于永远不应该在第一个类中调用此方法,因此我只需抛出一个带有消息的异常。这似乎没有组织。这绝对是错误使用继承。

如果我只在 Second 类中使用 methodB,而不将其添加到父类中:

 public static void main(String[] args) {
   Parent p = new Second();
   p.methodA() // Prints "This is method A"
   p.methodB() // throws an error
 }

有谁知道我如何调用类中的特定方法而不会引发错误。将方法添加到父级绝对是不好的做法。

编辑:

感谢所有快速响应,我发现这样做的方法是定义不同的接口:

interface X {
  public void A()
}

interface Y extends X {
  public void B()
}

class First implements X {
  @Override
  public void A();
}

class Second implements Y {
    @Override
    public void A();

    @Override
    public void B();
}

如果这仍然不正确,请告诉我。

【问题讨论】:

  • 我猜这违反了Liskov substitution principle。如果某些东西实际上不是更精致或更专业的子类型,则不应使用继承,并且如果这实际上不是所有子项的行为,则绝对不应将方法(即行为)放入父项中。重新考虑这些课程。 (另见en.wikipedia.org/wiki/Circle-ellipse_problem
  • 如果仅在单个子类中使用“不同的方法 B”,它是什么意思?如果它只在一个子类中,并且无论如何您都在覆盖方法 A,那么谁在乎方法中的内容是什么?如果子类不需要方法 B,为什么它会在父类中?
  • 如果其中一个子类需要方法 B,那么在我看来,方法 B 是该子类的私有方法,否则该子类并不是接口的真正实例。
  • 应该FirstSecond 实现Parent 吗?如果是这样,请写下来。
  • 在您删除之前,我设法在 cmets 中向我阅读了您的问题,并尝试回答(更新)

标签: java inheritance interface polymorphism abstract-class


【解决方案1】:

[...] 在父类中定义方法 B 是一种好习惯,即使它仅用于单个子类。

当然,不。

想象一下,如果您的超类的每个子类都添加自己的部分,您的超类将会增长得多么迅速。每个孩子都会有一堆其他子类需要的不相关的东西。它表明类之间的关系不正确,继承的使用不正确。

在你的 sn-p 中,类之间的关系是模糊的。继承总是需要真实的例子。在这里,我不能说Firstis aParent

对于将由几乎所有子类实现的方法,我已经看到了在父类中使用默认的基于异常的实现来定义该方法的做法。因此,您无需在子类中复制标准行为。

对你目前的情况会有一点改善:

default void methodB() {
     throw new UnsupportedOperationException("[...]");
}

Parent 实例传递给方法,请确保您将仅使用此类提供的接口。这将使您远离铸造和敲打它的界面。

A -> [methodA] (only)
B -> [methodB] (only)
C -> [methodA, methodB]

如果给定的方法还不够,请创建一个复合接口,添加错过的方法:

interface A { void methodA(); }
interface B { void methodB(); }
interface C extends A, B {}

class First implements A { ... }
class Second implements C { ... }

【讨论】:

  • 感谢代码示例的详细回答,现在更有意义了。
【解决方案2】:

interface 是实现它的类同意履行的契约。

考虑一个名为Furniture 的假设接口。不是所有的Furniture 都会合理地实现recline() 方法——比如一个表——但有些可能。这是一个很好的例子,为什么recline() 方法不应该出现在这个接口中,而应该出现在扩展接口RecliningFurniture 中。

这样,Table 类的对象将实现Furniture,但Chair 类的对象可能会实现RecliningFurniture。两者都将受Furniture 接口的合同约束,但Chair 还必须履行合同RecliningFurniture 的附加条款。

【讨论】:

  • 这么多为不同类别的子类定义不同的接口?这更有意义,感谢您的回答。
  • 是的,这是一种方式。您还可以创建一个实现您的接口的类,然后将该类扩展为子类。也许你想要一个抽象的椅子实现家具,然后你创建一个 RecliningChair 和一个 SwivelChair,以及一个 Stool,它们都扩展了椅子。只取决于您的项目以及您要完成的工作。
【解决方案3】:

如果某些对象的行为类似于 X 而其他对象类似于 X + 其他东西,则您需要 2 个接口,因为您有 2 个东西,其中一个与另一个相似,再加上一点

interface X {
      public void A()
}

interface Y extends X {
      public void B()
}

class First implements X {
      @Override
      public void A();
}

class Second implements Y {
      @Override
      public void A();
      @Override
      public void B();
}

在你需要 X 的情况下,所有的行为都会像 X 一样,这是肯定的。在其他情况下,您将不得不提到这里我们还有更多内容

【讨论】:

  • 感谢您的回答,这更有意义。
猜你喜欢
  • 2014-02-20
  • 1970-01-01
  • 1970-01-01
  • 2012-01-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多