设计原则—SOLID(SRP)
文章目录
- SOLID
- 单一职责原则
- 其他判断标准
- 过于单一的拆分
- 例子
- 序列化
- 例子
- 用户信息的例子
- 从实际场景的角度出发
- 从业务的角度出发
- 总结
SOLID
- 单一职责原则、开闭原则、里式替换原则、接口隔离原则和依赖反转原则,依次对应 SOLID 中的 S、O、L、I、D 这 5 个英文字母。我们今天要学习的是 SOLID 原则中的第一个原则:单一职责原则。
- 换个角度来讲就是,一个类包含了两个或者两个以上业务不相干的功能,那我们就说它职责不够单一,应该将它拆分成多个功能更加单一、粒度更细的类。
单一职责原则
- 单一职责原则的定义描述非常简单,也不难理解。一个类只负责完成一个职责或者功能。也就是说,不要设计大而全的类,要设计粒度小、功能单一的类
- 换个角度来讲就是,一个类包含了两个或者两个以上业务不相干的功能,那我们就说它职责不够单一,应该将它拆分成多个功能更加单一、粒度更细的类。
比如,一个类里既包含订单的一些操作,又包含用户的一些操作。而订单和用户是两个独立的业务领域模型,我们将两个不相干的功能放到同一个类中,那就违反了单一职责原则
- 单一职责原则的英文是 Single Responsibility Principle,缩写为 SRP。这个原则的英文描述是这样的:A class or module should have a single reponsibility。
- 这个原则描述的对象包含两个,一个是类(class),一个是模块(module)
- 一种理解是:把模块看作比类更加抽象的概念,类也可以看作模块。
- 另一种理解是:把模块看作比类更加粗粒度的代码块,模块中包含多个类,多个类组成一个模块
其他判断标准
- 类中的代码行数、函数或属性过多,会影响代码的可读性和可维护性,我们就需要考虑对类进行拆分;
那就是一个类的代码行数最好不能超过 200 行,函数个数及属性个数都最好不要超过 10 个
- 从另一个角度来看,当一个类的代码,读起来让你头大了,实现某个功能时不知道该用哪个函数了,想用哪个函数翻半天都找不到了,只用到一个小功能要引入整个类(类中包含很多无关此功能实现的函数)的时候,这就说明类的行数、函数、属性过多了
- 类依赖的其他类过多,或者依赖类的其他类过多,不符合高内聚、低耦合的设计思想,我们就需要考虑对类进行拆分;
- 私有方法过多,我们就要考虑能否将私有方法独立到新的类中,设置为 public 方法,供更多的类使用,从而提高代码的复用性;
- 比较难给类起一个合适名字,很难用一个业务名词概括,或者只能用一些笼统的 Manager、Context 之类的词语来命名,这就说明类的职责定义得可能不够清晰;
- 类中大量的方法都是集中操作类中的某几个属性,比如,在 UserInfo 例子中,如果一半的方法都是在操作 address 信息,那就可以考虑将这几个属性和对应的方法拆分出来
过于单一的拆分
- 单一职责原则通过避免设计大而全的类,避免将不相关的功能耦合在一起,来提高类的内聚性。同时,类职责单一,类依赖的和被依赖的其他类也会变少,减少了代码的耦合性,以此来实现代码的高内聚、低耦合。
- 但是,如果拆分得过细,实际上会适得其反,反倒会降低内聚性,也会影响代码的可维护性。
例子
序列化
- 我们对 Serialization 类进一步拆分,拆分成一个只负责序列化工作的 Serializer 类和另一个只负责反序列化工作的 Deserializer 类
public class Serializer {private static final String IDENTIFIER_STRING = "UEUEUE;";private Gson gson;public Serializer() {this.gson = new Gson();}public String serialize(Map<String, String> object) {StringBuilder textBuilder = new StringBuilder();textBuilder.append(IDENTIFIER_STRING);textBuilder.append(gson.toJson(object));return textBuilder.toString();}
}public class Deserializer {private static final String IDENTIFIER_STRING = "UEUEUE;";private Gson gson;public Deserializer() {this.gson = new Gson();}public Map<String, String> deserialize(String text) {if (!text.startsWith(IDENTIFIER_STRING)) {return Collections.emptyMap();}String gsonStr = text.substring(IDENTIFIER_STRING.length());return gson.fromJson(gsonStr, Map.class);}
}
- 虽然经过拆分之后,Serializer 类和 Deserializer 类的职责更加单一了,但也随之带来了新的问题。如果我们修改了协议的格式,数据标识从“UEUEUE”改为“DFDFDF”,或者序列化方式从 JSON 改为了 XML,那 Serializer 类和 Deserializer 类都需要做相应的修改,代码的内聚性显然没有原来 Serialization 高了。
- 如果我们仅仅对 Serializer 类做了协议修改,而忘记了修改 Deserializer 类的代码,那就会导致序列化、反序列化不匹配,程序运行出错,也就是说,拆分之后,代码的可维护性变差了
例子
用户信息的例子
public class UserInfo {private long userId;private String username;private String email;private String telephone;private long createTime;private long lastLoginTime;private String avatarUrl;private String provinceOfAddress; // 省private String cityOfAddress; // 市private String regionOfAddress; // 区 private String detailedAddress; // 详细地址// ...省略其他属性和方法...
}
- 一种观点是,UserInfo 类包含的都是跟用户相关的信息,所有的属性和方法都隶属于用户这样一个业务模型,满足单一职责原则
- 另一种观点是,地址信息在 UserInfo 类中,所占的比重比较高,可以继续拆分成独立的 UserAddress 类,UserInfo 只保留除 Address 之外的其他信息,拆分之后的两个类的职责更加单一
从实际场景的角度出发
- 我们不能脱离具体的应用场景。如果在这个社交产品中,用户的地址信息跟其他信息一样,只是单纯地用来展示,那 UserInfo 现在的设计就是合理的。
- 如果这个社交产品发展得比较好,之后又在产品中添加了电商的模块,用户的地址信息还会用在电商物流中,那我们最好将地址信息从 UserInfo 中拆分出来,独立成用户物流信息(或者叫地址信息、收货信息等)
- 如果做这个社交产品的公司发展得越来越好,公司内部又开发出了跟多其他产品(可以理解为其他 App)。公司希望支持统一账号系统,也就是用户一个账号可以在公司内部的所有产品中登录。这个时候,我们就需要继续对 UserInfo 进行拆分,将跟身份认证相关的信息(比如,email、telephone 等)抽取成独立的类
从业务的角度出发
- 如果我们从更加细分的“用户展示信息”“地址信息”“登录认证信息”等等这些更细粒度的业务层面来看,那 UserInfo 就应该继续拆分。
总结
- 我们可以总结出,不同的应用场景、不同阶段的需求背景下,对同一个类的职责是否单一的判定,可能都是不一样的
- 在某种应用场景或者当下的需求背景下,一个类的设计可能已经满足单一职责原则了,
- 但如果换个应用场景或着在未来的某个需求背景下,可能就不满足了,需要继续拆分成粒度更细的类。
- 在真正的软件开发中,我们也没必要过于未雨绸缪,过度设计。所以,我们可以先写一个粗粒度的类,满足业务需求。随着业务的发展,如果粗粒度的类越来越庞大,代码越来越多,这个时候,我们就可以将这个粗粒度的类,拆分成几个更细粒度的类。这就是所谓的持续重构(
设计原则—SOLID(SRP)相关推荐
- 六大设计原则SOLID
六大设计原则SOLID 一.SOLID 设计模式的六大原则 二.单一职责原则 (Single Responsibility Principle) 1. 单一职责原则定义 2. 问题由来 3. 解决方案 ...
- 流行20年的架构设计原则SOLID可能已经不适合微服务了
点击上方"服务端思维",选择"设为星标" 回复"669"获取独家整理的精选资料集 回复"加群"加入全国服务端高端社群「后 ...
- 【六大设计原则-SOLID】
SOLID简介: 历史:由Robert C·Martin汇总并推广 目标: 使软件更容易被改动 是软件更容易被理解 构建可以在多个软件系统中复用的组件 组成: 名称 简写 含义 单一职责原则 SRP ...
- 软件设计原则SOLID+组合聚合+迪米特原则(附代码讲解)
SOLID是五大设计原则的首字母简写,最早出现于出自Robert Martin(罗伯特. 马丁)的<架构整洁之道>第三章设计原则.他们分别是 single Responsibility P ...
- 设计原则——SOLID
SOLID是5个设计原则的统称,它们分别是:单一职责原则.开闭原则.里式替换原则.接口隔离原则和依赖反转原则,依次对应SOLID中的S.O.L.I.D. 1.单一职责原则 单一职责原则,Single ...
- 六大设计原则(SOLID)
一.SOLID 设计模式的六大原则有: Single Responsibility Principle:单一职责原则 Open Closed Principle:开闭原则 Liskov Substit ...
- 六大设计原则 (SOLID)
设计原则概述 设计模式中主要有六大设计原则,简称为SOLID ,是由于各个原则的首字母简称合并的来(两个L算一个,solid 稳定的),六大设计原则分别如下: 1.单一职责原则(Single Resp ...
- 设计原则—SOLID(LSP)
文章目录 里氏替换原则 落地 测试 反例 子类违背父类声明要实现的功能 子类违背父类对输入.输出.异常的约定 异常 输入 子类违背父类注释中所罗列的任何特殊说明 意义 对比多态 改进已有实现 指导程序 ...
- 设计原则—SOLID(DIP)
文章目录 依赖反转原则(DIP) 正确理解 优势 控制反转 例子 依赖注入(DI) 例子 依赖注入框架 对比基于接口编程 相同点 区别 总结 依赖反转原则(DIP) 高层模块(high-level m ...
最新文章
- Spring Security 进阶干货:自定义配置类入口WebSecurityConfigurerAdapter
- TensorFlow文本摘要生成 - 基于注意力的序列到序列模型
- 帧中继中配EIGRP(hub-spoke)
- 行业B2B网站策划6个基本要求
- [心得分享] 我在 GitHub 上学习开源
- jQuery 插入元素
- Android 关于长按back键退出应用程序的实现
- 移动网页如何实现发送短信和拨打电话的功能
- HTML5 canvas绘制雪花飘落动画(需求分析、知识点、程序编写分布详解)
- 从实现原理来讲,Nacos 为什么这么强
- idea lib下有jar包但是仍然报错 找不到类
- 会做饭的机器人曰记_会做饭的机器人作文二百字
- windows系统镜像文件汇总
- java 图片转pdf_在Java语言中将图像转换为PDF?Spire.PDF for Java轻松搞定!
- 经济机器是怎样运行的(文字版)-瑞.达利欧
- 算法竞赛资料整理分享
- webAppRootKey
- 召唤神龙无敌版------小鱼吃大龙
- 【老九学堂】【C++】CodeBlocks安装配置
- 留学生VS应届生,留学生在当下还有求职优势吗