mvc框架由model,view,controller组成,执行流程一般是:在controller访问model获取数据,通过view渲染页面。

mvc模式是web开发中的基础模式,采用的是分层设计,各层之间职责分明。然而事与愿违,当我们日积月累的基于mvc模式开发之后,会逐渐的感受到层与层之间存在粘连和职责模棱两可的地方,这就是service层出现的重要原因。

问题是什么

要提出解决方案,重要的是发现问题的本质。mvc模式在实践过程中,主要面临下面几个难受的问题:

  • 在C层直接实现业务逻辑,这将导致:

    • 不同的controller之间,无法共享通用的业务逻辑,比如:折扣计算,反作弊判定,这必然是不合理的。
    • 业务逻辑升级,需直接在原代码上做修改兼容,导致controller代码不断膨胀复杂。
    • 远程服务协议或者调用方式升级,需要找到所有controller里的调用点,逐一修改。
    • DAO发生替换(比如从oracle迁移mysql),需要找到所有controller里的调用点,逐一修改。
  • 在M层(DAO+model或者ActiveRecord,下面以model泛指)里实现业务逻辑,这将导致:
    • model承担了过多的业务逻辑,导致业务逻辑升级需要修改model,然而model的职责并不是业务,这是很矛盾的。
    • 调用1个model中的业务代码没有问题,但是遇到跨表事务又该由哪个model管理呢?
    • 业务逻辑实现在model中,如果model发生变更,那么里面写的业务逻辑也得粘贴复制到新的model中,这就是耦合的代价。

我仔细的回想了一下之前的MVC开发模式,上面的问题我几乎都遇到过并且试图解决过,比如:

  • 为了提升代码复用,我会把一些通用的功能实现为单独的工具类(校验登录),并在controller中提供调用。
  • 为了给controller提供业务相关的数据,我在ActiveRecord里实现了业务相关的增删改查方法供controller调用,更有意思的是:每次表变更字段,我通过工具gii重新生成ActiveRecord都会把我实现的方法覆盖,这就是耦合的代价。

问题的本质是:业务逻辑粘连了C层和M层,应该从C层&M层解耦出来,成为独立的Service层。由此,在C层可以灵活的替换Service保持高度的简洁,而M层保持职责单一仅仅为Service提供数据,Service层则实现所有复杂的业务逻辑与通用的业务逻辑。

Service层的职责

根据上面的分析,Service夹在C层和M层中间,从逻辑上大致划分为3大类:

  1. model侧的Service:也就是封装每个model与业务相关的通用数据接口,比如:查询订单。(我认为:访问远程服务获取数据也应该归属于这一类Service)
  2. 中间的Service:封装通用的业务逻辑,比如:计算订单折扣(会用到1中的Service)。
  3. controller侧的Service:基于1、2中的Service进一步封装对外接口的用户业务逻辑,当然也不排斥直接访问DAO而不使用上述2个Service(不建议)。

在实践中,应该会很自然的用到这三类Service,在了解了这些概念之后再进行代码设计,就不会对Service的职责产生困惑了,自然也对MVC有了新的认识。

关于抽象

Controller里调用"controller侧的Service"直接完成业务处理,意味着Controller依赖了具体是哪个Service类。

Service里调用"DAO/AR"实现数据库的访问,意味着Service依赖了具体是拿个"DAO/AR"类。

Service里调用Service,意味着Service依赖了具体是拿个Service类。

为了解除这种耦合,在Web领域一般采用的都是IOC依赖注入来实现"依赖反转",JAVA和PHP都可以基于反射实现这个能力,各个mvc框架都有相似的实现。

Service层是否必要呢?

见仁见智,我认为长时间维护的大型项目通过更精细的分层,更加有利于功能的迭代升级。

而对于中小项目,多一层就意味着更多的代码,而且在设计时还要考虑通用性以及通用性的粒度问题,还不如少动点脑子多写点冗余代码了。

最新文章

  1. [No000095].Net入门级逆向工程-1.SpreadsheetGear汉化
  2. sort()基础知识总结+超简短的英文名排序写法
  3. js+jquery的等价用法
  4. Java面试求职之==与equals()差别
  5. 23个mysql查询语句
  6. 搭建redis cluster
  7. POJ 1655 Balancing Act【树的重心】
  8. C语言程序第二次作业
  9. python中的图像数据库PIL
  10. angularJs实现数据双向绑定的原理
  11. c# 调试模式下Swaggerf附加接口参数
  12. 基于swagger进行接口文档的编写
  13. Gzip压缩和解压
  14. docker-compose批量管理docker容器
  15. python测试开发django-6.模板中include使用
  16. jQuery--事件, 事件绑定, 阻止事件冒泡, 事件委托,页面载入后函数
  17. Linux 下LNMP环境搭建_【all】
  18. WPF 任务栏颜色 - 简书
  19. PAT 甲级 1035 Password
  20. SQL2008R2 收缩数据库问题 - 日志文件不变小

热门文章

  1. 【167】IDL 中相关技巧
  2. 可视图linux--ubuntu的安装以及tool的安装
  3. PCB Genesis或Incam 右键导入TGZ 实现方法
  4. bzoj 4806: 炮【dp】
  5. CF17C Balance
  6. 构造 Codeforces Round #135 (Div. 2) B. Special Offer! Super Price 999 Bourles!
  7. js执行顺序——学习笔记
  8. EasyUI系列学习(九)-Panel(面板)
  9. LN : leetcode 241 Different Ways to Add Parentheses
  10. 解决安装androidstudio无法查看源代码的问题