Liskov替换原理VS接口隔离原理

时间:2019-12-17 10:26:27

标签: java oop liskov-substitution-principle interface-segregation-principle

在理解这两个原则时,我有些麻烦。这是一个长期阅读的问题,请耐心等待。

让我们假设我们有一个课

abstract class Shape {
    abstract void onDraw();
}

和界面

interface SideCountable {
  int getSidesCount();
}

然后我们创建两个子类

class Quad extends Shape {

   @Override
   public void onDraw() {
   //some draw logic here
   }
}

class Triangle extends Shape {

   @Override
   public void onDraw() {
   //some draw logic here
   }
}

现在我们将使Shape实现SideCountable

abstract class Shape implements SideCountable{
    abstract void onDraw();
}

并在儿童班上进行更改

class Quad extends Shape {

   @Override
   public int getSidesCount() {
       return 4;
   }

   @Override
   public void onDraw() {
   //some draw logic here
   }
}

class Triangle extends Shape {

   public int getSidesCount() {
       return 3;
   }

   public void onDraw() {
   //some draw logic here
   }
}

并为此结构创建测试功能(遵循LSP):

public void printSidesCount(List<Shape> shapes) {
    for(int i=0; i < shapes.size(); i++) {
        System.out.println(shapes.get(i).getSidesCount());
    }
}

在这里我想停止,因为实际上在下一步中我被卡住了。 如果我们要创建第三类Circle怎么办?

class Circle extends Shape {

   public int getSidesCount() {
       return ???;
   }

   @Override
   public void onDraw() {
   //some draw logic here
   }
}

圆没有边,因此为这个孩子实施SideCountable听起来很荒谬。好的,我们只能将实现转移到“四边形”和“三角形”,但是在这种情况下,LSP将不再起作用。有人可以描述我应该怎么做的最好方法吗?

  • SideCountable留在Shape类中,并为Circle返回0并打破接口隔离原则?
  • SideCountable移至QuadTriangle并打破LSP原理吗?

1 个答案:

答案 0 :(得分:2)

首先,您的方法printSidesCount只需要知道该列表包含SideCountable个对象。因此,为其参数指定类型List<Shape>比必要的更为具体。改用List<SideCountable>

public void printSidesCount(List<SideCountable> sideCountables) {
    for(int i=0; i < (); i++) {
        System.out.println(sideCountables.get(i).getSidesCount());
    }
}

甚至是List<? extends SideCountable>,它的意思是“实现SideCountable的任意未知类型的列表”:

public void printSidesCount(List<? extends SideCountable> sideCountables) {
    for(int i=0; i < sideCountables.size(); i++) {
        System.out.println(sideCountables.get(i).getSidesCount());
    }
}

如果不是所有形状的边数都可数,则类Shape不应实现接口SideCountable。相反,使类QuadTriangle除了扩展类SideCountable之外,还实现接口Shape

class Quad extends Shape implements SideCountable {
    // ...
}

class Triangle extends Shape implements SideCountable {
    // ...
}

并使类Circle扩展Shape但不实现SideCountable

class Circle extends Shape { // Not SideCountable
    // ...
}

当您这样做时,类型系统将为您提供帮助:

  • 您可以将List<Quad>List<Triangle>传递给printSidesCount,因为这些类型实现了SideCountable
  • 您不能将List<Circle>传递给printSidesCount,这很好,因为尝试这样做对圈子列表没有意义