里氏替换原则(lsp)在javascript中需靠设计自觉和运行时一致性保障,核心是子类必须严格遵循父类行为契约:输入不收紧、输出不缩水、异常不新增,并通过面向契约的组合与全覆盖测试验证替换安全性。

JavaScript 中没有编译期类型检查,也没有严格的继承语法约束(比如 extends 不强制契约),但里氏替换原则(LSP)依然关键——它不是靠语言机制兜底,而是靠设计自觉和运行时行为一致性来保障子类能真正“无声无息”替换父类。
用构造函数+原型链模拟继承时,先明确“可替换”的边界
不要只看能不能 new 出来、能不能调用同名方法,而要看调用方依赖了哪些隐含约定:
- 父类方法是否承诺返回非 null 值?子类不能突然返回
undefined或null - 父类方法是否声明会修改某个状态字段(如
this._loaded = true)?子类重写后不能跳过这步,也不能额外改其他字段 - 父类构造函数是否接受
options对象且允许部分字段缺失?子类构造函数不能突然要求所有字段必填
避免“伪继承”,优先用组合或接口式约定
JavaScript 没有原生接口,但可以用文档 + 运行时校验 + 类型工具(如 TypeScript)模拟:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 定义一个
Drawable行为契约:必须有render(ctx)和getBounds()方法,返回格式固定 - 让
Circle、Rect、Text各自实现,不继承同一个基类;使用时统一接收Drawable类型参数 - 客户端代码
canvas.draw(shape)就不会因某类“假装能画却抛错”而崩溃
重写方法时守住三条行为底线
即使语法上允许覆盖,逻辑上必须守约:
-
输入不收紧:父类
update(deltaMs)接受任意数字,子类不能加判断if (deltaMs —— 调用方传负值在父类中可能被忽略,子类突然报错就破坏 LSP -
输出不缩水:父类
getData()返回{ items: [], total: 0 },子类不能返回null或精简成[](除非父类契约明确允许) -
异常不新增:父类方法没声明会抛错,子类就不能在内部加
throw new NetworkError()—— 调用方没做 try-catch,换子类就崩
用测试验证替换是否真安全
写一组针对父类实例的单元测试(包括正常输入、边界值、意外输入),然后把测试对象换成子类实例再跑一遍:
- 相同输入下,输出结构、类型、值是否一致
- 是否多抛异常、少触发回调、漏发事件
- 对象自身状态变化是否符合原有语义(例如
save()后isDirty是否仍为false)
全通过,才说明这个子类真正满足 LSP。别依赖“看起来差不多”,要靠测试说话。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










