2019-11-05
我发现,一些Qt项目中,开发者因为Qt的框架提供了很好的解耦方式,便不再关注controller,把widget class 当作controller。这实在是不应该。在这样的代码基础上进行下去,就会发现所有的东西都逐渐放在了widget上。超大的cpp文件,ui、逻辑、数据库操作,混杂,不可直视。在这里,我们假设系统比较简单,MVC中Model包含了数据库访问(DAO),Controller包含业务层(如复杂业务算法或流程)。
我在之前的文章中提过:Qt本身弱化了Controller 概念,把Controller和View 放在一起了。这是合理的,因为signal/slot机制提供了强大的解耦能力。但是,这绝不应该是鼓励我们不重视独立的controller。其一,没有单独的controller,也会导致View包含了过多的业务限制。如果别的业务想要复用View,就会发现难以做到。其二,view包含了太多的东西,导致代码混乱,难以维护。
过分剥离View与Controller,就会走向另外一种极端。例如在桌面端程序中模仿Web项目中的View/Controller。要知道,web业务比多数桌面端的业务规则是要简单的。Web中的编程范式更像是函数式编程,注重数据的流向。但是,我们是难以看到函数式语言写桌面端项目的,天然不适合。桌面端的View/Controller有着更强的联系,一个View导致的Model层的变动,会牵连导致不少View变动。
合理的做法是,基本上,一个页面就会需要对应一个controller,特别是直接涉及到操作网络或者数据库的 页面。因为Qt 天然的让view承担了部分的controller职责,controller改动Model后,返回了新的Model,就可以在view层直接相互修改view,减少view/controller的耦合。
controller 职责:
-
收集数据,验证算法调用之前逻辑正确。
-
修改Model层
-
requestPage 权限检查
-
通知view层,或者其他controller的slot
根据职责,我们需要明确controller的依赖。
对于第一点,需要依赖view,也就是一些QWidget,因为需要提取各种 ui控件的信息,简单的操作当然可以通过参数struct直接传递给Controller,但是,极其复杂的UI数据,并不与Model层的数据完全对应,且包含递归关系,这样的数据,需要多少工作量来封装好并给入controller,controller 需要多少时间来解析呢?
第二点,我们需要把Model层的访问完全封锁在Controller层,例如DB、Redis,甚至其他各式数据形式。
第三点,我们的软件一般是需要权限控制的,虽然C++并不支持AOP,如Java中的annotation,但是,给Controller提供authority对象,让它进行权限控制,工作量稍微多一些,这样显然让模块关系更清晰写。
第四点,controller需要直接控制widget的更新。当然,仍然可以使用signal/slot机制来直接解耦。这里,controller可能需要依赖于其他的widget,以便做跳转,或者把requestPage 转发到更上层的专门负责页面跳转的controller。
对于view,我们可以少把业务规则放到里面。例如,一个widget能添加控件,业务规则只能添加N个,但是控制逻辑不能在view上实现,需要在controller中实现。都说了是业务规则,view只是体现业务规则,并非是对view本身的限制。这样,在复用时,就能尽量提高复用程度。我在另外一篇文章中写过,UI编辑器与手写代码之间也需要权衡。很多人不喜欢手写view代码,即使退而求其次,我也认为针对如下两点需求,一定要手写view代码:
-
view上业务逻辑比较复杂的,变动可能性较大的(意味着可能多人协同修改一个view)
-
会被重用的view。
我曾深深的被.ui 文件 conflict的恐惧所控制。此间有大恐怖。
P.S. 躺了两个月的草稿,终于发出来了。一下子整理了好几篇。说实在的,笔记本里数量都超过1000了,多数都是东一榔头,西一棒槌,看来,我之前的各种笔记很多,但是没有认真想过如何形成体系。
如果有任何意见,欢迎留言讨论。
[ 主页 ]