返回

单分派与双分派

images.png

面向过程 ——> 面向对象 : 提高代码的可复用性; 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

Licensed under CC BY-NC-SA 4.0