基于DCI架构的编程范式设计

近日随着对DCI编程范式的理解深入,结合java 8的语言特性,愈发觉得是时候可以把MVC架构放一放了。传统的mvc架构虽然结构清晰,但其肢解业务逻辑,使得开发跳来跳去,debug要像屡线头一样追踪代码,很是痛苦。而与MVC师出同门(同一理念提出者)的DCI架构,是基于用例驱动设计的,更加符合端侧用户的心智模型,也更符合程序员对于业务逻辑的理解,很容易上手。下面是我画的一幅丑图,并不规范,大概说明其结构含义即达到目的。

首先说D,即data objects,如图示,它是典型的“贫血模型”,特点是在不进入业务场景中时,它只描述了“”what the system is“”,即该用例系统的一个静态视角,这个用例里都有什么(在uml中system是一个用例的基本容器,至少有一个actor-case被包含在一个system中)。该模型中顶多会有一些私有的、操作成员数据的本地方法。除此之外无他,它只是起到了数据库的作用,用于维护对象状态。

其次是C,即Context,是码农随处可见的术语:上下文。它也可以表述成运行环境、业务场景。它的作用是触发一组业务逻辑(算法)的触发器,可以是main方法,可以是暴露的rest方法,RMI等,whatever.同时,它也是一个调度对象通信的组织者,不管是采用触发一个函数,递归式的链式调用栈,还是持有多个对象实例进行互相发消息,总之它是一个业务逻辑发生地,就像一场戏剧的演出舞台。

然后是I,即Interaction,交互。值得强调的是,编程是一种抽象层面、概念级的活动,如果说静态域模型是第一层抽象的话,那么Role角色这个概念就是在静态域模型的基础上又高了一个层次的抽象。我认为DCI模型的精髓也正在于此。即不同角色对象之间的通信完成了业务逻辑,而不是那个可怜的data object.举个例子,比如driverCar()开车这件事,是Driver司机开的,而不是那个叫person的pojo开的;司机这个角色是比人更高一层的抽象,如果训练有素,大金毛(一种巡回猎犬)也能开车扮演司机角色。而角色这个概念又是和上下文密不可分的,正是因为人进入了开车这个业务场景,它才扮演/充当了司机的角色;与此同时,司机执行了开车这个行为就构成了一个开车的场景。简言之,场景赋予普通对象角色,角色对象干了这个角色应该干的活儿就构成了场景。不同对象之间协作通信组网,就构成了交互——Interaction。

以上DCI的主要概念阐述完后,就是怎么应用问题了。那图示中的Mixin是怎么回事呢?mixin是混入的意思,在前端框架以及多继承、多分配类的语言中,是可以把抽离出来的功能注入给一个类中的,使其能达到重用和随时扩展功能的目的,一般这样的对象称其为mixin对象,它一般不单独使用,只是用来被混入到其他类中。而在java早期语言特性中,实现混入可能也只能通过extends或Aop来实现,比较笨重。自从java8支持FP后,如鱼得水。以上的mixin模块,就是指代一些被高频调用、无关业务逻辑、工具方法等类。如果说数据对象用来描述系统是什么、具有相对可靠的稳定性,而架构设计主要是为了解决和应对不稳定的需求变化的话,那么类比data object模块,mixin模块就是具有相对稳定性的行为接口,这不仅是一种数据结构和行为的分离,也是稳定部分和不稳定需求因素的分离。依照以上结构对一个用例进行分析抽象之后,就是由场景/上下文选择data object来扮演角色,添加角色对象职责或混入相应的行为(根据需要酌情)。至于角色对象与数据对象的关系是采用聚合还是组成,也是酌情。因此,有关职责分配,是需要谨慎考虑的。哪些方法是角色对象义不容辞的职责,就应该采用“充血模型”赋予其职责,这样的好处就是上下文内,只有对象存在,只有对象之间在"交谈",带来的是良好的代码可读性和算法的高内聚,非常清晰!关于可维护性,底层的data objects和mixin模块的静态、动态分离加上对象分内职责和混入功能的分离,也是其良好的保障基础;至于可扩展性,DCI这种基于用例驱动的设计,在扩展、更换一组上下文及其对象的操作上具有天然的优势。

一个完整的应用系统是由多个功能模块组成,每个模块又由多个用例即细分、扩展用例组成,这反映在DCI架构的项目中就应合理的划分模块、分包,以及合理的命名空间;对于上下文类的责任,如何结合rest请求去组织用例,例如门面模式还是分开映射,需要花些心思。

发布了24 篇原创文章 · 获赞 7 · 访问量 5328

猜你喜欢

转载自blog.csdn.net/GengMingHui_5/article/details/96162921