应用层次结构
应用分层的目的是什么?复用,解耦,提高代码的可读性,减少代码的维护成本。这些目的都很重要,大家也都清楚。我想说的是一个大家忽略的同时也是最重要的目的,就是减少同事代码归置出错的概率。这怎么解释呢?一个项目不是一人吃饱全家不饿的情况,都是大家协作完成。由于大家技术能力不大相同,特别是当有很多刚毕业的同事时,良好的层次结构能提供一种约束力减少代码归置出错的概率,并且在Review代码的时候,更加简单轻松,通过率更高。因此,以下任何一个依据都可以做作为分成的必要条件:
1. 复用,解耦,提高代码的可读性,减少代码的维护成本
2. 提高代码归置的约束力,减少自己和同伴代码归置出错的概率
- Controller(Action):
- 为公司App、公司Web、第三方提供的接口
- Facade:
- 主要为Controller服务
- 总领本领域的所有Service
- 提供给其他模块的Service引用
Service:
- 提供给其他sevice类平行引用,禁止相互引用
- 主要处理业务逻辑
- 处理业务聚合后的cache
- 如果service代码量过多,请阅读下面“值得玩味的service”的内容
其实这样也不能处理所有情况,更复杂的情况请看下面“值得玩味的Service”标题的内容。
InkService(Invoker Service):
- 屏蔽RPC接口的变化对项目的影响
- 封装invoker接口数据、缓存invoker接口数据
- 只处理invoker,invoker接口聚合,cache
- 能被多个模块的service引用
- Manager:
- 只处理db,cache
- 只能被本模块的Service引用
- Dao:
- 处理db
- Invoker:
- interface
- InvokerImpl:
- 简单的包装公司内部RPC接口
- Remoter:
- interface
- RmiImpl:
- 简单的包装公司内部RPC接口
- Component
- 主要包括一个模块中Service群共同依赖的Service方法
- 只能本模块Service引用,辅助Service执行去职能
- Helper:
- 只做共用数据打包和接口聚合逻辑,不做业务逻辑
- 只能本模块Service、Component引用
值得玩味的Service
如果Service的内容特别多,造成难维护的情况。有两种选择可以参考:
- 如果有必要,对目前的Service再进行domian划分,建立新的Service模块;
- 对目前的Service再进行更小的domian划分,分裂成多个Service。本领域的Service变多了也不好维护,并且相互依赖,怎么办?用Helper和
Component辅助Service执行其职能,Helper和Component的具体内容请看层次结构介绍。
代码逻辑分解推荐
代码分为业务处理逻辑、业务异常处理逻辑、其他异常处理逻辑、数据打包逻辑、数据获取和存储逻辑等等
上面的划分只是个人观点。有种说法是所有代码逻辑都是业务处理逻辑,我觉得概念太过笼统。我细分了一下这些概念,其中“业务处理逻辑”的概念只是业务变更的那段逻辑,这样做的目的是剥离出真正的业务最关注的代码部分。例如,报备楼盘到带看楼盘的过程中,“业务处理逻辑”的概念只是报备状态0便变成带看状态1。
Spring注释规则
- Controller:@Controller用于标注控制层组件(如struts中的action)
- Service:@Service用于标注业务层组件
- Manager、InvokerImpl、RmiImpl:@Repository用于标注数据访问组件
- Dao:@MasterMapper、@SlaveMapper 自定义关于MyBits选择库的注释
- Other:@Component泛指组件,当组件不好归类的时候,我们可以使用这个注解进行标注。
在目前的 Spring 版本中,这 3 个注释和 @Component 是等效的,但是从注释类的命名上,很容易看出这 3 个注释分别和持久层、业务层和控制层(Web 层)相对应。虽然目前这3个注释和 @Component 相比没有什么新意,但 Spring 将在以后的版本中为它们添加特殊的功能。所以,如果 Web 应用程序采用了经典的三层分层结构的话,最好在持久层、业务层和控制层分别采用上述注解对分层中的类进行注释。
实施难点
- 抽象domain,划分Service边界
- 各层次只做职能当中的事情,禁止跨层调用。如果有,请拿上你的案例和我讨论!
最大难点:你想不想做,去不去做?
常见问题
问题一
Q:把代码写在哪里才正确?
A:代码逻辑见上图
问题二
Q:如何抽象domian
A:详情请参考《大象-Think in UML》
问题三
Q:如何重构代码
A:详情请参考《重构-改善代码既有设计》
问题四
Q:如何避免大量service相互循环依赖?
A:目前service只允许循环平行引用,当出现相互循环依赖的问题时请考虑如下原因:
原因一:service的domian划分出现了问题,请重新考虑边界问题;
原因二:可以考虑抽象更加下层service领域,然后相互循环依赖的service再引用这个下层service;
如果还有其他原因,请带上案例来和我讨论吧!
编写代码时的“警惕”
- domian
- 全能函数
- 超过64K的文件类
- 长函数体
- 长函数名
- 不明其意的命名
- 命名困难
- switch-case/if-else等多个Type的问题
- 接口VS继承
- 用多态处理一些问题,想想设计模式
- 数据类是否是合理的冗余
- 尽量简单,只做一件事
更多警惕请见《重构-改善代码既有设计》的第三章,以及书本最后几页,以及《程序性能优化》第三章3.5节。