Yii框架的MVC实践的问题
一眨眼,使用Yii框架已经有两年的历史了。今天,改代码改得郁闷,我来吐吐槽。
Yii框架的官方文档,其实介绍了关于最佳实践的问题。就是Controller里该写些什么,Model里该写些什么,View里该写些什么。但是实际情况到底是怎么一回事呢?
Model,这里应该写什么,按照原有的最佳实践,这里应该实现所有的业务逻辑的。这个毋庸置疑。但是,Yii框架干了很浑的一个事情,就是做了一个ActiveRecord的东西,然后,各种案例教学,一搞Model这个问题的时候,好像就在讲这个AR怎么个好法。然后Model莫名其妙就跟DB搞一起了。然后大家就习惯性的提到Model总想到DB,再然后,Model就成了一个DB接口一样的东西。注意到这里的心态转变没有,Model跟业务逻辑的关系被淡化了。事实上,像我这样的公司,都不允许在Web服务器上连接DB,必须透过中间件,结果Model就惨了,彻底沦为了访问中间件的接口封装层。然后到底该如何实现业务逻辑呢?实现到什么粒度算是实现了业务逻辑呢?这个根本没有给出一个指导。比如,创建用户这个业务。你可以在Model里实现一个addUser的方法,把所有事情都做了。你也可以实现一个addUser,checkExist,sendEmail,然后在Controller里 if ($m->checkExist() && $m->addUser()) { $m->sendEmail() },你看,这到底算不算把业务逻辑写在了Model里呢?前一种,addUser在内部做完所有的事情,后一种,实现了三个子操作,在Controller里组装。前一种,Controller里干净了,就一行代码,但是复用性极差,因为别的场景,比如从文件向空DB导入用户的时候,就没必要sendEmail和checkExist,就无法复用第一种的代码,第二种的相对细粒度的,就容易复用。不是都说代码复用么,blahblah……结果人们很有很强的动力用第二种。但是,检查存在性,然后写入存储,最后发邮件,这个事情到底是不是业务呢?所以,Model里实现粒度的指导,至关重要。结果根本没有任何指导。最后,Model的实现,被彻底原子化了,而且粒度切分,按照个人的习惯而定,有的粗,有的细,最后,Controller就很恐怖地容纳了巨量的业务逻辑。
Controller,这里该写什么,其实是最模糊的一个地方了,其实正确的用法,我一直这么理解的,就是应该用Model实现一切业务逻辑,Controller里,就应该选择合适的Model,执行业务逻辑,然后将结果传递给用render渲染出来的View。在实际使用中是什么样子呢?比如有一个公用的预判条件,本来这种应该写在Model里的,但是很多Action需要这个预判,但是又不一定需要那个Model,结果这个预判被写在了Controller里面。甚至更广泛的一些判断条件,被写在了所有Controller的基类里。再比如,用户身份这个问题,应该封装在User组件里的,但是实际上,那样做理解起来太困难,很多人都搞不懂这个组件结构是怎么回事,然后为了方便,干脆把身份写在了所有Controller的基类里面,然后把身份id赋值给了Controller的公共变量。这简直就是灾难。
再说说这个View,这个本来是最简单的部分了。结果呢?按照条件展示页面的某个部分,总要判断某个条件吧,这个条件怎么来?应该在Controller里,由Model来计算。但是如果这个条件很通用呢?结果这个就被放到了Controller的基类里面。然后在View里直接调用Controller的函数,这个就是大错特错的一个事情。等于View知道了Controller的结构。最关键是,这个判断条件,根本就是业务逻辑的一部分。最后一团糟的就是连View里也间接透过Controller融入了业务逻辑。
以上我说的每种错误,都有看似简单易行的正确做法,但是为什么大家都没有去遵守呢?当所有人都犯错的时候,是不是该考虑一下,到底什么地方有问题呢?