青雲的博客

Article

里氏替换原则:子类换了父类,行为不能变

· 7 分钟阅读

里氏替换原则指出,子类对象应该能够替换父类对象,并且程序行为不会改变。郭靖的成长历程很好地体现了这一原则。

郭靖最初跟随江南七怪学习武功,江南七怪的武功可以看作是一种 “父类”。随着郭靖的成长,他又先后学习了洪七公的降龙十八掌等更高深的武功,这些可以视为 “子类”。但无论郭靖使用哪种武功,他的侠义之心和为人处世的原则始终不变,就如同在程序中,子类在替换父类后,整体的行为和功能不应发生改变。郭靖在战斗中,时而使用江南七怪所教的武功,时而施展降龙十八掌等绝学,但他所代表的正义、勇敢和坚韧的品质始终如一。 影视剧照:两名古装人物,前者双手结印于身前,后者立于其侧后方

子类替换父类之后,行为不能变

里氏替换原则强调:子类对象应该能够替换父类对象,并且程序行为不会改变。这意味着子类必须能够替代父类,并且能够保持父类的行为和属性。

示例:动物类和其子类

换成子类,行为不变

// 基类:动物
class Animal {
  makeSound(): void {
    console.log("Some generic animal sound");
  }
}

// 子类:狗
class Dog extends Animal {
  makeSound(): void {
    console.log("Barking");
  }
}

// 子类:猫
class Cat extends Animal {
  makeSound(): void {
    console.log("Meowing");
  }
}

换成子类试试

在遵循里氏替换原则的情况下,无论将基类 Animal 替换为什么子类(例如Dog、Cat),程序的行为都应该保持一致。

const animals: Animal[] = [new Dog(), new Cat()];

animals.forEach(animal => animal.makeSound());
// 输出:
// Barking
// Meowing

类图

里氏替换原则结构图:Animal、Dog、Cat 3 个角色,以及它们之间的继承关系

体现里氏替换原则的设计模式

  1. 策略模式(Strategy Pattern):策略模式通过将算法定义在独立的策略类中,使得可以在运行时互换这些算法。这些策略类通常是一个接口的不同实现,这样可以保证使用不同策略时的行为一致,体现了里氏替换原则。
  2. **装饰器模式(Decorator Pattern):**装饰器模式通过将附加功能封装到装饰器类中,使得可以在不修改具体组件代码的情况下,动态扩展对象的功能。这些装饰器类通常是具体组件类的子类,并且可以相互替换。
  3. **工厂模式(Factory Pattern):**工厂模式通过使用工厂方法创建对象,使得可以通过扩展工厂类或方法来创建新类型的对象,而不需要修改客户端代码。通过提供不同的具体产品类,工厂模式体现了里氏替换原则。
  4. **模板方法模式(Template Method Pattern):**模板方法模式通过将算法的步骤定义在基类中,而将具体实现延迟到子类中,使得子类可以替换父类以实现不同的算法步骤。
  5. **观察者模式(Observer Pattern):**观察者模式通过定义一个观察者接口,使得不同的观察者实现可以相互替换,从而保证在主题通知改变时,不同的观察者能够一致地响应,符合里氏替换原则。

RxJS、Redux 与 Immutable.js

RxJS

RxJS 是一个用于异步编程的库,通过 Observable 模式实现了许多功能,其中 Observable 接口和不同的操作符类都能够互相替换,实现了一致的行为,完全遵循里氏替换原则。 示例

import { of } from 'rxjs'
import { map } from 'rxjs/operators'

const obs$ = of(1, 2, 3)
obs$.pipe(map((x) => x * 2)).subscribe(console.log) // 输出: 2, 4, 6

Redux

Redux 是一个状态管理库,遵循里氏替换原则,其 Store 和 Reducer 接口可以通过不同的实现方式互相替换,而不会影响系统的整体行为。 示例

import { createStore } from 'redux'

interface Action {
  type: string
}

const reducer = (state = 0, action: Action) => {
  switch (action.type) {
    case 'INCREMENT':
      return state + 1
    case 'DECREMENT':
      return state - 1
    default:
      return state
  }
}

const store = createStore(reducer)

store.dispatch({ type: 'INCREMENT' })
console.log(store.getState()) // 输出: 1

Immutable.js

Immutable.js 提供不可变的数据集合,这些集合类(如 ListMap 等)都遵循里氏替换原则,能够互相替换而不改变系统行为。 示例

import { List } from 'immutable'

const list = List([1, 2, 3, 4, 5])

list.forEach((value) => console.log(value)) // 输出: 1, 2, 3, 4, 5

六大原则总览与其余各篇:总览单一职责开闭依赖倒置接口隔离迪米特法则

Keep Reading

相关文章

评论