软件开发中的单一职责

[http://www.infoq.com/cn/articles/single-responsibility-in-software-development](<http://www.infoq.com/cn/articles/single-responsibility-in-software-development>)

最近在实践微服务化过程中,对其“单一职责”原则深有体会。 那么只有微服务化才可以单一职责,才可以解耦吗?答案是否定的。

单一职责原则是这样定义的:单一的功能,并且完全封装起来。

我们做后端Java开发的,应该最熟悉的就是标准的3层架构了,尤其是使用Spring.io体系的:Controller,Service,Dao/Repository。为什么要分层?就是为了保证单一职责,数据模型的事情交给Controller,业务逻辑的事情交给Service,和数据打交道的事情就交给Dao/Repository。有时候或者有些人会分层分的更多,4层,5层,我自己也这样干过,说白了也是为了保证单一职责,3层不能满足单一职责了,耦合度高了,就分。

我们都知道一个webapp在经过一定时间的开发后,就惨不忍睹,即便是有标准的分层,页面或模板文件一大堆,最初的很清晰的3层标准架构也变味了,Controller,Service,Dao/Repository各层之间、Service之间、Dao/Repository之间互相调用,一团乱麻。这个时候没改一行代码都有可能一个老鼠害了一锅汤,bug就如同蚂蚁洞。

这些问题最后就造成:

可扩展性灵活性差,出现性能问题

业务变更和开发困难,维护成本很高,交付时间长

回归测试量很大

...

为了解决这些问题,就需要时时刻刻清楚的记住“单一职责”,单一职责可以用到软件开发的任何地方。

应该说职责分离来解耦是最常用最有效的架构方法,这能够很大限度的简化一切。

下面就从软件开发、设计、架构,以及重构/演进/进化,从小到大几个方面来说说单一职责:

类方法/函数

这应该是最小的能体现单一职责的程序单元了。最熟悉的最典型的莫过于Helper/Utils类方法了,但这种类方法的特征很明显,也很容易遵循单一职责,99%以上的开发人员都可以做到。但不仅仅这样的类方法要遵循单一职责原则,每一个类方法都应该遵循单一职责原则,尤其是一些处理业务逻辑的类方法更要遵循单一职责原则,处理业务的类方法通常要配合类的单一职责原则进行,下节中讨论。

因此,这也是为什么很多TL要求类方法代码行数保持在20行左右,其实就是为了保证单一职责,20行左右是一个经验粗略数字,当然,10行或者30行来完成类方法也是可以的。大部分单一职责的类方法用20行左右的代码就够了,如果超过20行就要考虑是否保证了单一职责了。那我们在迭代重构的过程中就要考虑拆分这样的类方法来保证单一职责。

类方法的单一职责是最单纯的,很具体的,不惨杂任何额外信息,只关心输入、输出、和职责;一定要明确地定义类方法的职责,保证在迭代中不被错误的扩张,调用方错误的使用。

类/函数文件

要用面向对象的设计方法,单一职责原则来定义类。开发人员一定要很好地理解“单一职责原则”,具有面向对象的抽象思维能力。

当在迭代中一个类过于庞大或者快速膨胀,说明已经有坏味道了,这时候就需要考虑用单一职责原则或者面向对象的分析方法来重构和重新定义类了,通常就是要抽象和拆分类,否则将来会变成一个方法容器。

把类比作一个人,她的职责就是完成自己职责范围内的事情,如果她什么事情都管,就叫多管闲事,可以想象她多管闲事的后果了,会搅得鸡犬不宁。同样,类也是,类如果多管闲事,那会搅得真个应用不稳定,漏洞百出,还很难修复。所以说定义一个类,要明确这个类的职责。使用面向对象的分析和设计方法,能很好地准确定义一个类的职责范围,通常就要用到封装、继承、多态和抽象等设计方法。

包结构/文件夹


分层就是最常用的架构方法之一,分层具体体现在分包和分类,就是分门别类的意思。俗话说,物以类聚,人以群分。

包结构在单一职责原则上是类的补充,职责范围进一步扩大。如果把一个类叫做一个人,那么包就是一个最小单位的团队,职责就是负责一类特定事情。 如何分包呢?那就要用到分类学的知识了,要以什么特征来分,可能不仅仅只有一种特征,比如,先用公司域名来做基础包名,这里叫一级包名;然后再用一个特定的有意义的标识作为二级子包名;之后按分层(web,dao,service等等)方法做三级包名,也可以先按照业务再按分层。例如:

域名:tietang.wang

有个项目叫:social

那么我可以这样分:

wang.tietang

       - social

- web

- service

- dao

- commons

也可以这样:

wang.tietang

- commons

- user

- web

- service

- dao

- relation

- web

- service

- dao

多工程/module

通常以多maven module或者gradle 多module形式存在,来保证单一职责。 当业务量还没有达到服务拆分的火候,又需要规整项目结构,通常在一个app发展的太庞大时或者在工程建设初期采取,从文件系统上隔离,通过module依赖来集成。需要注意的是这样的架构或拆分不是随意的,要以单一职责原则来拆分,更具体一点就是要根据业务,技术框架功能等特性来拆分。

比如,按技术组件拆分,通常会有一些技术组件,可以把她放到commons module,如果有多种类型的技术组件,就拆分为commons module的子module;也可以直接将这些技术组件拆分为独立的工程,存在于独立的git/svn仓库,独立管理,专人负责;其他哪些module需要就依赖她。那拆分的这些技术组件的每一个应该遵循单一职责原则,例如数据分片的框架,NIO基础网络框架等等。

比如,按业务拆分,例如有用户,订单,商品,支付,那么就按照这些业务拆分为子module,每一个子module就只负责自己的业务逻辑,也遵循单一职责。

那每个module的职责范围又比类和包更大,这个时候职责也更模糊,有时候很难把握,对于技术组件可能相对清晰,业务module就要熟悉业务,明确业务边界。

多module拆分后也是为将来服务化埋下伏笔,同时在物理文件系统比较清晰了,那在依赖管理上也要掌握好保持清晰的依赖逻辑,把握好单一职责原则。

微服务/可部署单元

微服务,从运行时隔离,但业务量发展到一定时候,从单体或者多module工程拆分或演化出来,可独立打包可独立部署并复合单一原则的application,当然了微服务所体现的价值不仅仅是隔离和独立部署,还有很多这里可以参考单体应用与微服务优缺点辨析。单一职责在微服务中的价值是最重要的,包含了app层面和开发app的团队层面,微服务的大部分优点都可以围绕单一职责来张开。

团队

先引用《韩非子·扬权》中的一段文字:

夫物者有所宜,材者有所施,各处其宜,故上下无为。

使鸡司夜,令狸执鼠,皆用其能,上乃无事。

上有所长,事乃不方。

矜而好能,下之所欺:辩惠好生,下因其材。

上下易用,国故不治。

参考:

原文:http://www.shici8.com/bookview_3501.html

译文:http://www.shici8.com/article_8539.html

各得其所,各司其职。所以,团队也要遵循单一职责原则,这样才能很好地管理团队成员的时间,提高效率。一个人专注做一件事情的效率远高于同时关注多件事情的。同样一个人一直管理和维护同一份代码要比多人同时维护多份代码的效率高很多。每一个人都有自己的个性,他有自己的擅长,让每一个人专注自己擅长的事情,那肯定事半功倍,整个团队绩效肯定也很突出。

总之,引用古文名句说明了所有:

物以类聚,人以群分。

天下之事,分合交替,分久必合,合久必分!
使鸡司夜,令狸执鼠,皆用其能,上乃无事。

参考:

http://www.jianshu.com/p/f9d15827465d

https://zh.wikipedia.org/wiki/单一功能原则


个人博客地址:

http://tietang.wang/2016/06/28/%E6%8A%80%E6%9C%AF/%E8%BD%AF%E4%BB%B6%E5%BC%80%E5%8F%91%E4%B8%AD%E7%9A%84%E5%8D%95%E4%B8%80%E8%81%8C%E8%B4%A3/

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,398评论 25 707
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,585评论 18 139
  • “微服务架构”这一术语在前几年横空出世,用于描述这样一种特定的软件设计方法,即以若干组可独立部署的服务的方式进行软...
    ThoughtWorks阅读 16,887评论 1 71
  • 亲爱的学姐学长你们好 很高兴我们有缘在美好的大学校园里相遇,我想这是一种冥冥之中的缘分! 对于学习,我...
    原来是雪姑娘阅读 1,808评论 2 4
  • 在工作中经常用修改StatusBar的背景和字体颜色,下面介绍一下StatusBar到底为何物?从整个UIWind...
    BernardChina阅读 647评论 0 0