这里的“Web 控制器”指的的确是 MVC 中的 Controller,在 Django 等 Python Web 框架中也被称为“视图”。
说到本文,源起 Python Web 框架中对于“控制器”有两种不同的表达方法。其中一种是类似于 Rails 的 class-based,另一种是 类似于 Sinatra 的 function-based(当然 Sinatra 的实际是 block-based)。tornado、web.py 采取了前者,而 Flask、Bottle、Django 采取了后者。
在组织简单代码的时候,两种方式仅仅是风格上的区别,这时候往往 function-based 会显得更加简洁。但对于更加复杂一点的情况,class-based 的控制器有一些代码组织上的优势,可能 function-based 的需要花一些脑筋才能达到相同效果。
举个例子,在 tornado 中如果要实现 Google 的 OpenID 登录,可以将和 OpenID 相关的逻辑分离成一个 GoogleMixin 类,然后混入到控制器中。这样组织出来的代码更加有条理。事实上,tornado 也自带了 GoogleMixin 类, 官网文档 中有个非常好的使用范例:
从范例可以看到,这种 class-based 的控制器不仅能将某一个主关注点的代码分离,还能将细节步骤以类成员函数的形式写出来,代码条理性非常好。但是如果是 function-based 的控制器呢?恐怕就不像写 hello, world 的时候那么舒服了。
分析一下造成这种差异主要的主要原因:
- 类可以以继承的方式复用代码,函数不行
- 类可以包含多个成员函数组织代码,函数只能借助全局作用域的其他函数
所以,现在要做的是提出对策:
- 函数可以以装饰器的形式复用代码
- 装饰器可以写成类的形式,以求分离成员函数,提高代码条理性
用伪代码表达这两个对策就是:
这么一来代码就更加有清晰了,实现了“复用”和“分离”两个目标。如果结合比 Class Request Context 更灵活的 LocalThread Context,完全可以将复杂的 function-based Web 控制器组织得比 class-based 的更有条理。