LSP-Liskov替代原理 - deepdive


Liskov替换原则是SOLID的一部分,该缩写缩写总共捆绑了5条设计原则。
它通常与干净的代码相关联。
但是到底是什么,对您来说重要吗,您甚至应该关心吗?
 
它是什么?
如果S是T的子类型,则可以用类型S的对象替换类型T的对象,而无需更改程序的任何所需属性(正确性,执行的任务等)。
听起来很复杂,对吧?
但是可以归结为一个简单的定义:
 软件(系统)应由可互换的零件构建。这些部分应在共同合同上达成共识,以使这些部分可以互相替代。
也许还不那么容易,但是让我们更进一步,从JavaScript的角度来看一下:

  • override覆盖基类方法的子类方法必须具有完全相同数量的参数
  • 覆盖方法的每个参数必须与基类的方法具有相同的类型
  • 覆盖基本方法的方法的返回类型必须为相同类型
  • 只能抛出与基类相同类型的异常

 
例子1
此示例违反了LSP,因为子类FriendlyGreeter向greet方法添加了另一个参数。
class Greeter {
  greet(name) {
    return `Hello, ${name}!`;
  }
}

class FriendlyGreeter extends Greeter {
  greet(name, age) {
    return `Hello, ${age} year old ${name}, have a nice day!`;
  }
}

 
例子2
在此示例中,子类Penguin引发了基类不知道的错误,并且该类的用户也不知道该错误Bird。
尽管此问题是不太明显的问题之一,但实际上是对LSP的常见侵犯!

class Bird {
  fly() {
    return "I'm flying!";
  }
}

class Penguin extends Bird {
  fly() {
    throw new Error(
"I wish I could but I can't. :(");
  }
}
 

它试图防止什么?
该原则试图保护您和您的用户免受意外行为的侵害。
您知道这会导致什么吗?=>您或库创建者可以创建基本逻辑,并依靠合同!
该合同规定了参数,返回类型以及可能引发的错误。有了这些,您就可以实现实际上可以准确地处理此=>的逻辑,仅此而已。
如果编写基本逻辑的人指定可以抛出CannotDoError,则在使用该逻辑或从该逻辑派生该逻辑时只需要处理此错误,而不是在该逻辑旁边可能出现的许多错误。
只要用户同意合同,他们在您身边的意外行为就不会有任何问题。这非常棒,因为您的软件可以按预期运行!
 
你应该在乎吗?
哦,是的,你应该!
开发可靠且可扩展的软件,而这不会令任何人感到惊讶,这将是您的主要目标。
所有这些导致了开发速度,而速度导致了功能被更频繁地发布!