软件开发中的单一职责(转至INFOQ)

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

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

我们做后端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%以上的开发人员都可以做到。但不仅仅这样的类方法要遵循单一职责原则,每一个类方法都应该遵循单一职责原则,尤其是一些处理业务逻辑的类方法更要遵循单一职责原则,处理业务的类方法通常要配合类的单一职责原则进行,下节中讨论。

因此,这也是为什么很多Team Leader要求类方法代码行数保持在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的团队层面,微服务的大部分优点都可以围绕单一职责来展开。

团队

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

夫物者有所宜,材者有所施,各处其宜,故上下无为。
使鸡司夜,令狸执鼠,皆用其能,上乃无事。
上有所长,事乃不方。
矜而好能,下之所欺:辩惠好生,下因其材。
上下易用,国故不治。

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

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

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

参考

时间: 2024-10-17 20:38:24

软件开发中的单一职责(转至INFOQ)的相关文章

对于软件开发中开发人员与测试人员关系的理解

在软件开发中都会有开发人员(以下简称开发)和测试人员(以下简称测试),在一些小型公司可能并没有测试,仅仅是开发兼任测试.在这里我仅针对于有专业的测试和专业的开发的项目. 每个公司应该都有考核机制,对于开发和测试的考核实际上很难量化,通常来讲大的方向就是开发所负责模块的bug数,对于测试来讲就是测出来的bug数,但这真的有效吗?这也许对开发有约束力,理论上开发是能够自己控制bug数的,如果从产生的bug数来评判开发的绩效还算有效,这样开发自然就会把代码写得更加认真.但如果根据测试测出来的bug数来

Atitit. 软件开发中的管理哲学--一个伟大的事业必然是过程导向为主 过程导向 vs 结果导向

Atitit. 软件开发中的管理哲学--一个伟大的事业必然是过程导向为主    过程导向 vs 结果导向 1. 一个伟大的事业必然是过程导向为主 1 1.1. 过程的执行情况(有明确的执行手册及标准) 2 1.2. 麦哲伦的事例证明了过程导向的重要性 2 1.3. 婆罗门教大师商接罗所 过程导向,属于上梵,结果导向,属于下梵 2 1.4. 罗马皇帝诚思录上,说人生本身就是过程导向, 2 1.5. 过程导向基本属于战略层面,侧重与长期,而结果导向只是战术级别,短期容易短视 3 1.6. 任何组织的

对软件开发中uml建模的理解和图形整理(一)

由于uml(统一建模语言)在开发中经常会用到,特别是在软件开发中的OOAD阶段,因此要理解和使用uml显得尤为重要.在uml开始之前,咱先回顾一个OOAD.OOP的主要特征. OOAD:根据面向对象的方法学来对软件系统进行分析和设计的过程.它包括OOA 分析阶段和OOD设计阶段.其中分析阶段主要解决"What to do?"的问题,而设计阶段主要解决"How to do?"的问题.具体来说就是:在OOA分析阶段咱要做的主要工作就是建立对业务问题域的视图(建立模型).

软件开发中几个基本概念

软件开发中几个基本概念 Peixu.Zhu 自己真的深切理解那些经常挂在嘴边的概念么? 抽象 Abstract 抽象的特点是仅存在于思想和理论之中,而非物理或者具体的存在.(不是指C++中的抽象类) 抽象是永存的,不会随着时空而发生变化. 具体 Concrete 具体的特点是物化的或者是具备物理形态,是真实存在的. 具体不是永存的,是随着时空而发生变化的,仅存于具体的时空之中. 具体和抽象的最大区别是是否随着时空而发生变化,即是否存在于我们的四维空间. 实体 Entity 实体是单独的个体事物(

软件开发中,什么是模块化开发?

软件产品可以被看作是由一系列具有特定功能的组件组成,作为一个完整的系统也可以被分解成一系列功能模块,这些模块之间的相互作用就形成了系统的所有功能. 所谓模块是指可组成系统的.具有某种确定独立功能的半自律性的子系统,可以通过标准的界面和其他同样的子系统按照一定的规则相互联系而构成的更加复杂的系统.每个模块的研发和改进都独立于其他模块的研发和改进,每个模块所特有的信息处理过程都被包含在模块的内部,如同一个"黑箱",但是有一个或数个通用的标准界面与系统或其他模块相互连接. 在软件的模块化开发

软件开发中的自测及C代码示例

在软件开发中,程序自测是一个永远都绕不开的话题.很多开发人员以写出有难度的代码为荣,但却不重视对自己编写的代码进行测试,这导致了最终到达客户手中的产品质量不高,bug频发,损害了公司的形象.对于一个开发人员来说,我们应该将开发和自测置于同等重要的地位,我们花在自测上的时间要不比开发少.能否对自己编写的代码进行充分的自测也是检验一个开发人员水平高低的标准之一. 自测方法 根据所编写的程序的特点,自测方法大致有如下几种: 第一种,利用模拟工具进行自测.这种方法适用于需要其他模块(尚不具备)发过来的消

基于git的软件开发中并行工程管理以及版本控制系统概要

并行工程师什么,这里就不再解释(不懂请百度),实际上,在软件开发过程中,涉及到多人合作的以项目小组形式完成开发的软件(这里指广义上)或多或少都使用了并行工程的概念,在正式的项目开发中,项目小组成员总是分工合作每人完成一部分,然后再合并起来,而且,在实际应用中,尽管使用的是瀑布模型完成开发,但总是所有项目小组成员同时开始完成自己的部分,这,其实已经是并行工程了,我们可以自豪的宣布:我们在开发过程中使用了并行 工程这种高大上的玩意来提高开发速度,所以,老板你得给我们涨工资! 很简单吧,看起来好简单的

软件开发中的流程图

在软件开发中必须经历五个阶段,当然这仅是我个人的看法,我只是个初学者,步骤如下: 1.需求分析 2.算法设计极其分析 3.编写代码 4.测试代码 5.软件维护 对于初学者来说,第5步,可以暂时不用管,当然我们的需要任务就是学好第一步,需求分析,有时候一个软件的开发花费的大量时间并不在于编写代码上,而真正花费时间的是第一步,我们软件开发人员开发的软件并不是为了我们,而是为了客户,因此我们要想开发一个成功的软件,了解客户的需求是非常必须的,客户并不是真正的开发人员,他们的要求没有编程人员的想法严密,

软件开发中 常见英文文档 缩写(转)

软件开发中常见英文缩写和各类软件开发文档的英文缩写: 英文简写 文档名称 MRD market requirement document (市场需求文档) PRD product requirement document (产品需求文档) SOW 工作任务说明书 PHB Process Handbook (项目过程手册) EST Estimation Sheet (估计记录) PPL Project Plan (项目计划) CMP Software Management Plan( 配置管理计划