古语有云:工欲善其事,必先利其器。对于Web开发亦是如此,不过现在的Web框架实在是太多了!以PHP为例,有CakePHP、CodeIgniter、Symfony,Zend,Yii等等,到底谁是最合适的?事实上过多的选择往往会让人陷入「乱花渐欲迷人眼」的窘境,这些年我一直游走在各种PHP框架之间,却始终没有觅得属于自己的屠龙刀,于是我决定自己动手,就像歌里唱的那样:不是你亲手点燃的那就不能叫做火焰。
既然要自己动手,那么就需要明确一下设计目标,我个人主要关注以下几个方面:微内核、模块化、扩展性。套用陈道明说过的一句广告语:简约而不简单。
在说明设计方案之前,不妨放松一下心情,聊聊看似风马牛不相及的太阳系:
Solar System
在我小时候,课本上教的是太阳系有九大行星,分别是:水星、金星、地球、火星、木星、土星、天王星、海王星和冥王星。其中,冥王星较小,后来,人们发现还有一些和冥王星类似的天体也在围绕太阳运转,如果冥王星被划为行星的话,那么这些天体无疑也属于行星,此时太阳系到底有几大行星的问题彻底让天文学家蒙圈了,最后他们耍了一个小聪明,剥夺冥王星的行星资格就行了,从此太阳系就只剩八大行星了。
当然,我并不是让你关注这些天文学上的八卦往事,实际上我希望你关注的是太阳系的结构模式:有的行星是丁克家庭,没有卫星,比如水星和金星;有的行星是计划生育标兵,比如地球只有一个卫星(月亮);有的行星是超生游击队,卫星多的数不胜数,以至于起名时只能以阿拉伯数字加以区分,比如木星和土星。卫星沿着自己的轨道围绕着行星旋转,行星沿着自己的轨道围绕着太阳旋转,这就是太阳系!
如果你了解设计模式的话,那么你会惊讶的发现,上帝是一个设计模式大师,他在设计宇宙的结构时,使用了装饰器模式:月亮装饰着地球,地球装饰着太阳,甚至太阳系本身也是银河系的一个装饰器,如此循环,这就是宇宙!
既然上帝只用了装饰器这一个概念便创造了整个世界,那么我们能不能利用装饰器模式创造Web框架呢?实际上类似的框架在Python社区中早就有了,可惜PHP社区却好像始终无动于衷,我在几年前做过一些粗浅的尝试,但是由于种种原因搁置了,最近借着公司一个项目的改版,我终于完成了它,并命名为「Beahoo」,它是一个迷你框架,代码极简,很好的诠释了我前面提的目标:微内核、模块化、扩展性。
我们先来看看控制器中Action的代码实现:
<?php
namespace Beahoo\Controller;
abstract class Action
{
protected $decorators = array();
public function build(Action $action = null)
{
if ($action === null) {
$action = $this;
}
$decorators = array();
for (
$class = get_class($action);
$vars = get_class_vars($class);
$class = get_parent_class($class)) {
if (empty($vars['decorators'])) {
continue;
}
foreach ($vars['decorators'] as $decorator => $name) {
if (is_int($decorator)) {
list($decorator, $name) = array($name, null);
}
if (!isset($decorators[$decorator])) {
$decorators[$decorator] = $name;
}
}
}
$core = $action;
$decorators = array_reverse($decorators);
foreach ($decorators as $decorator => $name) {
if ($name === false) {
continue;
}
$decorator = $this->load($decorator, $action);
$action = $this->build($decorator);
if ($name) {
$core->{$name} = $decorator;
}
}
return $action;
}
protected function load($class, $args = null)
{
if ($args === null) {
return new $class;
}
if (!is_array($args)) {
$args = array($args);
}
$reflection = new \ReflectionClass($class);
return $reflection->newInstanceArgs($args);
}
abstract public function execute(
Request $request,
Response $response
);
}
我们再来看看控制器中Decorator的代码实现:
<?php
namespace Beahoo\Controller;
abstract class Decorator extends Action
{
protected $action;
public function __construct(Action $action)
{
$this->action = $action;
}
public function getNextAction()
{
return $this->action;
}
public function getLastAction()
{
$action = $this->action;
while (is_subclass_of($action, __CLASS__)) {
$action = $action->action;
}
return $action;
}
public function execute(Request $request, Response $response)
{
$this->action->execute($request, $response);
}
}
本文省略了细枝末节的代码实现,只保留了最关键的Action和Decorator,几百行代码就实现了DNA双螺旋结构,只要有了它们,便可以衍生出丰富多彩的生命形式。
让我们扮演一把上帝,看看如何利用装饰器模式创建太阳系:
<?php
namespace SolarSystem\Controller\Action;
use Beahoo\Controller\Request;
use Beahoo\Controller\Response;
class SunAction extends \Beahoo\Controller\Action
{
protected $decorators = array(
'SolarSystem\Controller\Decorator\NeptuneDecorator',
'SolarSystem\Controller\Decorator\UranusDecorator',
'SolarSystem\Controller\Decorator\SaturnDecorator',
'SolarSystem\Controller\Decorator\JupiterDecorator',
'SolarSystem\Controller\Decorator\MarsDecorator',
'SolarSystem\Controller\Decorator\EarthDecorator',
'SolarSystem\Controller\Decorator\VenusDecorator',
'SolarSystem\Controller\Decorator\MercuryDecorator',
);
public function execute(Request $request, Response $response)
{
// ...
}
}
装饰器本身也可以被装饰器装饰,比如地球和月球的关系就是个极好的例子:
<?php
namespace SolarSystem\Controller\Decorator;
use Beahoo\Controller\Request;
use Beahoo\Controller\Response;
class EarthDecorator extends \Beahoo\Controller\Decorator
{
protected $decorators = array(
'SolarSystem\Controller\Decorator\Earth\MoonDecorator',
);
public function execute(Request $request, Response $response)
{
// ...
}
}
怎么样?是不是有种造物主的成就感,太阳系就在我们手中,设想一下:如果有一颗彗星正在穿越太阳系会发生什么情景,那么就让我们运行它试试吧:
<?php
$sun = new \SolarSystem\Controller\Action\SunAction;
$sun->build()->execute($request, $response);
虽然本框架在代码实现上秉承了极简的原则,但是在功能上却毫不逊色,篇幅所限,我无法一一说明,有兴趣的读者不妨自己探索一下吧。