重点 — 将子类替换掉,也不影响原程式运作
- 继承原class的子类,在程式中被替换为子类,也不会影响其运作。
- 常用的情况是在「替换呼叫模组」,将类别(class)换成其他子类,程式也可以正常运行。
举例 & 限制
参考 设计模式 禅的解说,因应此原则,父子 class 会有一些「限制」:参数输入和回传型别
- 参数输入限制:父 class 的参数「范围」,要比子 class 的参数还小。
- e.g. 父 class 只能接受 string ,而子 class 倒是可以接受 string | number,这样在主程序传入 string ,替换为子 class 就没问题
- 反过来说,如果父 class 是 string | number,子 class 只能接受 string,那么在主程序传入 number ,替换为子 class 就型别报错,出事情了!
- 回传型别限制:父 class 的回传「范围」,要比子 class 的参数还大。
- e.g. 这就比较直觉了,父 class 回传 string | number,替换为回传 string 的子 class ,不会有问题。
- *即便是 JavaScript 这种没型别的,也需要遵守此规则(但没型别检查,因此容易违反!)
举例实作
-
class
-
子类
type State = Record<string, any>;
interface BasicState {
state: State;getState(): State;
}interface MemberStateObj extends State {
account: string;
}
class MemberState implements BasicState {
state: MemberStateObj;constructor(state: MemberStateObj) {
this.state = state;
}getState() {
return this.state;
}
}interface OrderStateObj extends State {
items: any[];
transportation: number;
}
class OrderState implements BasicState {
state: OrderStateObj;constructor(state: OrderStateObj) {
this.state = state;
}getState() {
return this.state;
}
} -
使用子类的class
class StateManager {
stateHolder: BasicState;constructor(stateHolder: BasicState) {
this.stateHolder = stateHolder;
}getStateKeys() {
return Object.keys(this.stateHolder.getState());
}
}const stateManager = new StateManager(
new MemberState({
account: \'aaa111\',
})
);
stateManager.getStateKeys(); // [ \'account\' ]stateManager.stateHolder = new OrderState({
items: [],
transportation: 20,
});
stateManager.getStateKeys(); // [ \'items\', \'transportation\' ]
-
-
function
-
子类function
function calc(calcFn: Function, ...nums: number[]): number {
return calcFn(nums);
}function calcSumV2(...nums: number[]): number {
return calc((_nums: number[]) => _nums.reduce((p, n) => p + n, 0), ...nums);
}function calcMutiply(...nums: number[]): number {
return calc((_nums: number[]) => _nums.reduce((p, n) => p * n, 1), ...nums);
} -
Calculator
function Calculator(
this: {
calcFn: (...nums: number[]) => number;
getResult: Function;
},
calcFn: (...nums: number[]) => number,
...nums: number[]
) {
this.calcFn = calcFn;
this.getResult = () => this.calcFn(...nums);return this;
}const _calculator = new (Calculator as any)(calcSumV2, 1, 2, 3, 4, 5);
_calculator.getResult(); // 15(1+2+3...)_calculator.calcFn = calcMutiply;
_calculator.getResult(); // 120(1*2*3...)
-
应用
- 可以应用在模组替换、服务模组解耦等情境。
- 其实跟依赖注入很像,都是靠着参数callback的方式,将多个「具体实例」作为参数,传入其中呼叫使用
REF
- https://en.wikipedia.org/wiki/SOLID
- https://www.pythontutorial.net/python-oop/python-liskov-substitution-principle/
- https://dev.to/cleancodestudio/liskovs-substitution-principle-python-design-patterns-31c2
- https://zh.wikipedia.org/wiki/里氏替换原则