面向过程 ——> 面向对象 : 提高代码的可复用性; if语句 ——> 多态 : 提高代码的可维护和可扩展性(同时提高了程序的效率,因为工厂类创建对象后,调用该对象的方法就无需条件判断了)
##何为分派 分派就是根据类型去选择调用哪个函数。
像C 或者 JAVA 这种静态语言支持多态,天然支持分派,静态语言我们只能在运行时自行判断,一个简单的例子如下。
function foo(value) {
if(value instanceof Array) {
// do something
} else if(value instanceof Date) {
// do something
} else if (value instanceof Number || value instanceof String) {
// do something
} else {
// do something
}
}
这种不算复杂还好,但是如果多个参数,多种类型,这一个函数的 if 分支判断就会越来越多越来越难以维护。
我们可以借鉴一下 Python 中的解决方案,Python 通过单分派泛函数部分支持了方法重载。
单分派函数
我们可以实现一个简单的 singleDispatch 用 Map 存储注册的参数类型,通过 register 链式调用,来注册类型及其对应处理函数。
function singleDispatch() {
const registry: Map<string, Function> = new Map();
const res = (...args: any) => {
const types = args.map((arg: any) => {
const constructor = Object.getPrototypeOf(arg).constructor
return Object.getOwnPropertyDescriptors(constructor)?.name?.value || ''
});
const dispatcher = registry.get(types.toString())
if (dispatcher) return dispatcher?.(...args);
}
res.register = (types: string[], dispatch: Function) => {
if (!Array.isArray(types)) throw Error(`${types} not array!`);
if (typeof dispatch !== 'function') throw Error(`${dispatch} not function!`);
registry.set(types.toString(), dispatch)
return res
}
return res;
}
使用方法:
function test(name: number): number;
function test(name: string): string;
function test(name: number | string) : number | string{
return singleDispatch().register(['Number'], () => {
console.log('Number', name)
return name
}).register(['String'], () => {
console.log('String', name)
return name
})
}
你可以在此基础上实现更高级的功能,比如泛函数,any 类型等等。
双分派(double dispatch)
分派过程就是确定一个方法调用的过程,双分派就是见人说人话,见鬼说鬼话,将静态绑定和动态绑定结合起来,也就是说根据运行时传入对象的类型确定方法调用的过程。
重载是静态绑定,多态是动态绑定(运行时进行),双分派把多态放在重载之前,以实现在运行时动态判断执行那个子类的方法。因为编译器知道this
所指向的实例的类型,因此将其作为参数传入到函数中就可以实现。
class Man : public Base {
public:
int getSpeakRes(SpeakBase& speak) {
return speak.speakWord(this); //编译器知道this是哪个类型实例
}
}
class Ghost : public Base {
public:
int getSpeakRes(SpeakBase& speak) {
return speak.speakWord(this);
}
}
class Speak : public SpeakBase {
public:
int speakWord(Man& man) {
return "Man";
}
int speakWord(Ghost& ghost) {
return "Ghost";
}
}
class Board {
drawFigure(aFigure) {
aFigure.getDrawnOn(this)
}
drawTriangle(aTriangle) {
// ...
}
drawSquare(aSquare) {
// ...
}
}
class Triangle extends Figure {
getDrawnOn(aDrawableSurface) {
aDrawableSurface.drawTriangle(this)
}
}
class Square extends Figure {
getDrawnOn(aDrawableSurface) {
aDrawableSurface.drawSquare(this)
}
}
----
let board = new Board();
let triangle = new Triangle();
board.drawFigure(triangle)
参考文章:
https://www.cnblogs.com/loveis715/p/4423464.html http://yaoyao.codes/c++/2015/04/26/cpp-double-dispatch https://www.typescriptlang.org/docs/handbook/functions.html#overloads https://www.mikealche.com/software-development/refactor-long-if-or-switch-blocks-with-double-dispatch-in-javascript