代码是否违反了 Liskov 替换原则?

does the code violate the Liskov Substitution Principle?

我在尝试理解 Liskov 替换原则时遇到了一些困难,想知道以下代码是否违反了 Liskov 替换原则?

public class Task {

     String status = "Ready"; // One of "Ready", "Started", and "Closed"

     public void setStatus(String newStatus) {
          status = newStatus;
     }
     public void cancel() {
          status = "Closed";
     }
}
public class ProjectTask extends Task {

     @Override
     public void cancel() {
          if (status.equals("Started")) {
               throw new RuntimeException("Cannot cancel a started project task.");
          }

          super.cancel();
     }
}

我认为它确实如此,因为 subclass 在被替换时不像基 class 那样表现,还因为它抛出 RunTimeException?

我不完全确定,想知道我的假设是否正确

subclass 的行为方式不必与 base class 相同。它必须执行基础 class' 合同。

很遗憾,您没有记录基础 class' 合同,所以我不能说它是否正确。如果是这样的话:

public class Task {
    ...

    /**
     * Attempt to cancel the task.
     *
     * @throws RuntimeException if the task is not in a cancellable state
     */
    public void cancel() {
        status = "Closed";
    }
}

……那就好了

合同意味着任何调用 Task.cancel 的人都需要预料到异常。

你看,LSP 不仅仅是基础 class 的作用,或者子 class 的作用。是关于使用这些东西的代码。

LSP 表示,当声明一个方法或构造函数以 Task 作为参数时,声明承诺它不仅适用于直接 Task 实现,而且适用于所有有效的子 class 实现,因为 ProjectTaskTask.

作为工程师,您的工作就是确保兑现这些承诺。