单一职责原则
1429字约5分钟
设计模式
2022-04-08
简介
单一职责原则(SRP, Single responsibility principle)一个对象应该只包含单一的职责, 并且该职责被完整地封装在一个类中.
单一职责原则分析
一个类(达到模块, 小到方法)承担的职责越多, 它被复用的可能性就越小.
当一个职责变化时, 可能会影响其他职责的运作.
将这些职责进行分离, 将不同的职责封装在不同的类中.
将不同的变化原因封装在不同的类中.
单一职责原则是实现高内聚、低耦合的知道方针.
举例
需求: 读取hive数据库, 将读取到的数据每行以dict格式输出(dict key为字段名, value为每行字段的值)
以单一职责原则为目标, 我们应该先考虑将需求分成两块:
- 读取hive库
- 按行转换dict
这样设计出来两个方法:
select_a_table_Hive(table_name)
list_to_dict(line_list)
第一个方法和第二个方法都只包含单一的职责, 查单表, list转换dict结构. 这样如果有其他模块需要使用查表的功能可以选择调用select_a_table_Hive
函数, 需要list转dict时候可以调list_to_dict
函数, 达到复用的目的. 对于系统上更好实现应该有hive操作模块统一负责涉及hive的操作, 设计数据库操作接口控制hive、pg、Oracle等数据库, 有utils工具包来负责基础数据的转换.
试想如果用我们把整个方法放在一个函数内实现, 这可以达到需求的目的, 但是由于职责的耦合当需求变更时候, 客户需要以list形式输出, 或者希望postgresql的查询结果转成dict, 这个函数就必须修改重新调试, 或者写一个新的方法实现需求, 简单的方法可能可以很快调试通过, 但是对于复杂的系统, 每次变更对于耦合的方法很难下手修改.
单一职责的优点
组织代码 让我们想象一个汽车修理工. 他使用很多工具一起工作. 这些工具按类型分为: 钳子, 螺丝刀(十字/刀片), 锤子, 扳手(管/六角)等, 他如何组织管理这些工具呢?他使用许多小抽屉将这些工具分门别类存放, 这些抽屉其实类似模块作用, 专门用来管理各种类.
减少脆弱 当一个类有多个理由需要修改时, 它变得脆弱, 在一个地方的修改会导致其他地方不可预期的后果.
更松耦合 更多职责责任导致更高的耦合; 耦合也是一种责任; 高度耦合导致高度依赖, 意味着难以维护.
代码改变 对于单一职责模块重构更容易. 如果你想获得猎枪的效果, 就让你的类有更多职责.
维护性 维护一个单一职责的类比维护一个铁板一块的类更容易.
易于测试 测试单一目标的类只需要很少的测试类.
易于调试 在一个单一职责类找到问题是一件更容易的事情.
如何识别SRP被破坏?
类有太多依赖 类的构造器有太多参数, 意味着测试有太多依赖, 需要制造mock太多测试输入参数, 通常意味着已经破坏SRP了.
方法有太多参数 类似类的构造器, 方法参数意味着依赖.
测试类变得复杂 如果测试有太多变量, 意味着这个类有太多职责.
类或方法太长 如果方法太长, 意味着内容太多, 职责过多. 一个类不超过 200-250
描述性名称 如果你需要描述你的类 方法或包, 比如使用"xxx和xxx"这种语句, 意味着可能破坏了SRP.
低聚合Cohesion的类 聚合Cohesion是一个很重要的概念, 虽然聚合是有关结构概念, 但是聚合和SRP非常相关, 如前面论坛案例, 如果一个类不代表一个高聚合, 意味着低凝聚low Cohesion, 它就可能意味破坏SRP. 一个低凝聚的特点: 一个类有两个字段, 其中一个字段被一些方法使用;另外一个字段被其他方法使用.
在一个地方改动影响另外一个地方 如果在一个代码地方加入新功能或只是简单重构, 却影响了其他不相关的地方, 意味着这个地方代码可能破坏了SRP.
猎枪效果Shotgun Effect 如果一个小的改变引起一发动全身, 这意味SRP被破坏了.
不能够封装模块 比如使用Spring框架, 你使用@Configuration or XML 配置, 如果你不能在一个配置中封装一个Bean. 意味着它有太多职责, Spring配置应该隐藏内部bean, 暴露最少接口, 如果你因为多个原因需要改变Spring配置, 可能破坏了SRP.
会中讨论结论:
写方法的上下文, 特别是需要其他模块调用的接口, 需要满足SRP;
方法传参可以写(self,name=zzc,age=18)这样的方式;