代码设计中几种全局hook的方式

我们在维护项目时经常要开发全局hook类的需求,比如程序启动时我们需要开启Session,记录Log,打开各种连接。程序关闭时我们需要关闭Session,计算请求耗时,回收内存,关闭各种连接等。

基于全局hook的需求繁多,OOP类的语言都会提供大量的魔术方法,在应对庞大项目时也能得心应手。说两个常见的方法:

  1. 构造函数;

    构造函数能在我们程序启动时开启Session,记录Log等;

  2. 析构函数;

    析构函数在我们程序结束时关闭Session,计算请求耗时,回收内存。

有人可能会问程序都是按顺序执行的,我们完全可以在代码的最开始执行这些初始化工作,为什么要多此一举在构造函数中做?原因是当项目庞大之后我们会针对Session与Log做大量初始化工作,并将这些工作当作组件封装,组件会对开发者透明,程序员只有在继承类中调用。

通过魔术方法来实现hook很简单,属于比较早期的方法,适用的场景有限。随着项目的代码量增加,程序员们发现魔术方法已经不能满足我们复杂的需求,于是纷纷想出各种招数。

一. 在MVC的控制器中抽象出hook方法

PHP的Yii2框架中就是通过beforeAction与afterAction来实现全局的hook,Model层也有对应的filter与事件触发器。为什么Yii2框架不直接使用魔术方法,而是通过代码封装实现hook方法呢?因为Yii2在设计之初就在魔术方法中使用了大量的动态延迟绑定方法,做了大量的初始化工作,这部分工作对开发者来说也是透明的。再比如我们需要在某些基类中实现单例模式,而单例模式很重要的一点就是将构造函数私有。诸如此类限制,通过beforeAction与afterAction来实现全局的hook是非常应景的一种方式。

二. 通过中间件抽离hook方法

在MVC的控制器中抽象出hook的方法非常方便,但是耦合度太高,不够直接。比如我们对访客做鉴权,禁止无权限的用户访问某些方法,这部分工作在路由转发时就能做,不用在控制器中处理。所以程序员们又想出了Middleware(中间件)的方式实现hook。比如在Laravel中,Middleware与控制器同级并实现了完全解耦,这样在做路由转发时就能调用Middleware去做大量工作。不方便的地方就是控制器与Middleware传值麻烦,不过将控制器细分是好事。况且还有Model层对应的filter与事件触发器来处理数据。

三. 通过设计模式实现实时hook

在程序之前和之后完成hook工作并不能完全满足我们的需求,我们终极的想法是在任何地方都能方便的实现hook。而实现hook最难处理的就是参数传递与共享,基于此程序员们不停的总结,最后创造出了『设计模式』。所以说设计模式大部分就是为了完成我们复杂的hook需求。所以你会看到路由、控制器、Model,View层都有对应的事件触发器,各种过滤器。