为什么不选Yii框架?

以前,我也注意到,不在现有项目中引入框架是有原因的,而且,尤其不能选用 Yii 框架。

“继承”噩梦

你所有的 controller,都继承自 CController,其又继承自 CBaseController,这个又继承自 CComponent。

所有你的 model,都继承自 CActiveRecord 或者 CFormModel,它们又继承自 CModel,这个 CModel 也继承自 CComponent。

这两者的继承链条,都包含了静态变量,并且执行了其他很多类的静态方法。这两个因素造成调试过程变得极为困难和冗长。

全局状态

有多种形式的全局状态。PHP 程序员熟知的就是全局变量。但是这并不是唯一的一种形式。类中的静态变量,也是一种全局状态,它(总是)能够造成看起来没有关系的类实例神秘地交互。

(Yii 框架)对全局状态的使用是其核心机制。在代码中,你到处都能看到静态方法的调用,而且,Yii 的配置文件,离了全局状态就没法工作。

每次你调用 Yii::app()的时候,你都在访问或者改变它。

这使得统一测试你的 Yii 应用,成为了不可能。而调试工作,则变成了练习在你的整个项目中使用 grep。

紧耦合

当你使用 Yii 创建一个应用的时候,如果不启动整个框架,你是不可能只运行其中一部分的。大多数时候,取决于你最终在你的代码里用了静态方法。

每次你在你的代码里添加了一个静态方法的调用,这段代码就跟那个静态方法的类耦合起来了。这就是紧耦合。

你可能已经注意到了(我希望你注意到了),有其他的方法可以实现一样的效果,使用 new 操作符。这是另一个方法让你的代码跟某个特定的类耦合起来。没有接口。

无论 Yii 项目的配置文件看起来有多吓人,这个配置文件的设计仍然是用心良苦的。这是在本已经一团糟的代码里,再度引入外部代码并代替已经存在的组件的伤害最小的一种方式。

然而,它将(Yii 框架)缺少接口和存在耦合带来的问题带到了聚光灯下。

很多程序员想要替换的组件之一是 CUrlManager,大多数是因为你能够传递额外的参数(给它)的方式的原因。

OOP 中的接口,明确了两个实例间的约定。由你来定义实例的能力,能被别人调用的方法。如果一个大的代码里,没有接口的话,你就不得不猜测,哪些方法是必须的,哪些不是。

在 Yii 组件里面,问题甚至更加严重,因为使用了静态调用和深度继承。上文提到的 CUrlManager 继承了 CApplicationCompnent,它又继承了 CComponent。而 CUrlManager 的同一个文件里,还定义了 CUrlRule 和 CBaseUrlRule。

当你编写一个他们的替代品的时候,你必须写一些代码,将他们插入到配置文件中,并在你自己的 Application 中测试,使用这种方式,你才知道下一个需要你填写的方法是什么。

基本上来说,这是一种“保存,然后看看什么东西爆掉了”的研发方法。

这绝不是 MVC!

Yii 并没有实现 MVC 或者任何受 MVC 启发的设计模式。它叫做“MVC”的东西,实际上可以用“ActiveRecord-Template-Logic”模式来描述。

Yii 框架的作者,实现了一组 active record 和表单封装,而不是一个恰当的模型层,这导致应用的逻辑必须写在 controller 里面。

另一方面,你有被美化的模板,而不是包含展示逻辑的适当视图实例。这在一定程度上被小部件的使用所缓解,但小部件反而遭受单一职责原则(SRP)的侵犯,因为它们被迫包含部分展示逻辑并执行部分渲染。剩余的展示逻辑又一次最终在控制器中。

更糟糕的是,“控制器”还必须处理授权问题。这通常意味着,每当你更改访问方案时,你将不得不检查每一个CController的实例,以确定是否也需要更改。

这不是MVC。这是一个混乱,名字是从MVC设计模式中借来的,随意贴在一些组件上。
所有的小事情…

框架也有一些小问题,不值得单独成章:

定义一个文件中多个类:

这很快就会变得令人烦恼,因为有些类被硬塞到完全不相关的文件名中。这意味着,调试往往需要使用搜索。

随意添加的“模块”:

看起来,这些模块是在事后添加到框架中的。这就是为什么,当你需要设置默认模块时,你将不得不在配置文件参数中设置,该参数被称为’defaultController’。