【设计模式之美】理论一:怎么才算是单一原则、如何取舍单一原则

2024-01-04

开始学习一些经典的设计原则,其中包括,SOLID、KISS、YAGNI、DRY、LOD 等。
本文主要学习单一职责原则的相关内容。

单一职责原则的定义:一个类只负责完成一个职责或者功能。也就是说,不要设计大而全的类,要设计粒度小、功能单一的类。

比如,一个类里既包含订单的一些操作,又包含用户的一些操作。而订单和用户是两个独立的业务领域模型,我们将两个不相干的功能放到同一个类中,那就违反了单一职责原则。为了满足单一职责原则,我们需要将这个类拆分成两个粒度更细、功能更加单一的两个类:订单类和用户类。

一. 如何判断类的职责是否足够单一?

大部分情况下,类里的方法是归为同一类功能,还是归为不相关的两类功能,并不是那么容易判定的。如下举个例子:创建一个UserInfo类

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 中拆分出来,独立成用户物流信息(或者叫地址信息、收货信息等)。

综上所述,评价一个类的职责是否足够单一,我们并没有一个非常明确的、可以量化的标准,这是件非常主观的事情。

持续重构

实际上,在真正的软件开发中,我们也没必要过于未雨绸缪,过度设计。所以,我们可以先写一个粗粒度的类,满足业务需求。随着业务的发展,如果粗粒度的类越来越庞大,代码越来越多,这个时候,我们就可以将这个粗粒度的类,拆分成几个更细粒度的类。这就是所谓的持续重构。

下面这几条判断原则,比起很主观地去思考类是否职责单一,要更有指导意义、更具有可执行性:

  1. 类中的代码行数(200行)、函数或属性过多(10个以上),会影响代码的可读性和可维护性,我们就需要考虑对类进行拆分;
  2. 类依赖的其他类过多,或者依赖类的其他类过多,不符合高内聚、低耦合的设计思想,我们就需要考虑对类进行拆分;
  3. 私有方法过多,我们就要考虑能否将私有方法独立到新的类中,设置为 public 方法,供更多的类使用,从而提高代码的复用性;
  4. 比较难给类起一个合适名字,很难用一个业务名词概括,或者只能用一些笼统的 Manager、Context 之类的词语来命名,这就说明类的职责定义得可能不够清晰;
  5. 类中大量的方法都是集中操作类中的某几个属性,比如,在 UserInfo 例子中 ,如果一半的方法都是在操作 address 信息 ,那就可以考虑将这几个属性和对应的方法拆分出来。

实际上, 从另一个角度来看,当一个类的代码,读起来让你头大了,实现某个功能时不知道该用哪个函数了,想用哪个函数翻半天都找不到了,只用到一个小功能要引入整个类(类中包含很多无关此功能实现的函数)的时候,这就说明类的行数、函数、属性过多了。

二. 类的职责是否设计得越单一越好?

为了满足单一职责原则,是不是把类拆得越细就越好呢?答案是否定的。

Serialization 类实现了一个简单协议的序列化和反序列功能。

/**
 * Protocol format: identifier-string;{gson string}
 * For example: UEUEUE;{"a":"A","b":"B"}
 */
public class Serialization {
  private static final String IDENTIFIER_STRING = "UEUEUE;";
  private Gson gson;
  
  public Serialization() {
    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 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 类。

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 高了

实际上,不管是应用设计原则还是设计模式,最终的目的还是提高代码的可读性、可扩展性、复用性、可维护性等。我们在考虑应用某一个设计原则是否合理的时候,也可以以此作为最终的考量标准。

参考:《设计模式之美》–王争

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【设计模式之美】理论一:怎么才算是单一原则、如何取舍单一原则 的相关文章

  • 基于成本和服务质量考虑的不确定性下,电动汽车充电网络基础设施需求预测和迭代优化的分层框架研究(Python代码实现)

    欢迎来到本博客 博主优势 博客内容尽量做到思维缜密 逻辑清晰 为了方便读者 座右铭 行百里者 半于九十 本文目录如下 目录 1 概述 2 运行结果 3 参考文献 4 Python代码 数据
  • Navicat 16 for MySQL:打造高效数据库开发管理工具

    随着数据的快速增长和复杂性的提升 数据库成为了现代应用开发中不可或缺的一部分 而在MySQL数据库领域 Navicat 16 for MySQL作为一款强大的数据库开发管理工具 正受到越来越多开发者的青睐 Navicat 16 for My
  • 【计算机毕业设计】个人日常事务管理系统

    进入21世纪网络和计算机得到了飞速发展 并和生活进行了紧密的结合 目前 网络的运行速度以达到了千兆 覆盖范围更是深入到生活中的角角落落 这就促使 管理系统的发展 管理系统可以实现远程处理事务 远程工作信息和随时追踪工作的状态 网上管理系统给
  • 【计算机毕业设计】校园体育赛事管理系统

    身处网络时代 随着网络系统体系发展的不断成熟和完善 人们的生活也随之发生了很大的变化 人们在追求较高物质生活的同时 也在想着如何使自身的精神内涵得到提升 而读书就是人们获得精神享受非常重要的途径 为了满足人们随时随地只要有网络就可以看书的要
  • 【计算机毕业设计】线上招聘问答系统

    计算机网络发展到现在已经好几十年了 在理论上面已经有了很丰富的基础 并且在现实生活中也到处都在使用 可以说 经过几十年的发展 互联网技术已经把地域信息的隔阂给消除了 让整个世界都可以即时通话和联系 极大的方便了人们的生活 所以说 线上招聘问
  • 【计算机毕业设计】北关村基本办公管理系统

    在如今社会上 关于信息上面的处理 没有任何一个企业或者个人会忽视 如何让信息急速传递 并且归档储存查询 采用之前的纸张记录模式已经不符合当前使用要求了 所以 对北关村基本办公信息管理的提升 也为了对北关村基本办公信息进行更好的维护 北关村基
  • 5个步骤,教你瞬间明白线程和线程安全

    记得今年3月份刚来杭州面试的时候 有一家公司的技术总监问了我这样一个问题 你来说说有哪些线程安全的类 我心里一想 这我早都背好了 稀里哗啦说了一大堆 他又接着问 那你再来说说什么是线程安全 然后我就GG了 说真的 我们整天说线程安全 但是对
  • 38条Web测试经验分享

    1 页面链接检查 每一个链接是否都有对应的页面 并且页面之间切换正确 可以使用一些工具 如LinkBotPro File AIDCS HTML Link Validater Xenu等工具 LinkBotPro不支持中文 中文字符显示为乱码
  • 线程安全(中)--彻底搞懂synchronized(从偏向锁到重量级锁)

    接触过线程安全的同学想必都使用过synchronized这个关键字 在java同步代码快中 synchronized的使用方式无非有两个 通过对一个对象进行加锁来实现同步 如下面代码 synchronized lockObject 代码 对
  • 2024年金三银四网络安全考试试题

    2023年金三银四网络安全考试试题 1 关于数据使用说法错误的是 A 在知识分享 案例中如涉及客户网络数据 应取敏感化 不得直接使用 B 在公开场合 公共媒体等谈论 传播或发布客户网络中的数据 需获得客户书面授权或取敏感化 公开渠道获得的除
  • 【网安神器篇】——WPScan漏洞扫描工具

    目录 一 Wordpress简介 二 WPScan介绍 三 安装 四 获取token 1 注册账号 2 拿到token 五 使用教程 1 常用选项 2 组合命令 1 模糊扫描 2 指定扫描用户 3 插件漏洞扫描 4 主题漏洞扫描 5 Tim
  • 【计算机毕业设计】趵突泉景区的智慧导游小程序_5ztvv

    当今社会已经步入了科学技术进步和经济社会快速发展的新时期 国际信息和学术交流也不断加强 计算机技术对经济社会发展和人民生活改善的影响也日益突出 人类的生存和思考方式也产生了变化 传统趵突泉景区的智慧导游采取了人工的管理方法 但这种管理方法存
  • 【计算机毕业设计】宝鸡文理学院学生成绩动态追踪系统

    研究开发宝鸡文理学院学生成绩动态追踪系统的目的是让使用者可以更方便的将人 设备和场景更立体的连接在一起 能让用户以更科幻的方式使用产品 体验高科技时代带给人们的方便 同时也能让用户体会到与以往常规产品不同的体验风格 与安卓 iOS相比较起来
  • 【计算机毕业设计】springbootstone音乐播放器的设计与实现

    随着我国经济的高速发展与人们生活水平的日益提高 人们对生活质量的追求也多种多样 尤其在人们生活节奏不断加快的当下 人们更趋向于足不出户解决生活上的问题 stone音乐播放器展现了其蓬勃生命力和广阔的前景 与此同时 为解决用户需求 stone
  • 网络安全基础知识面试题库

    1 基于路由器的攻击手段 1 1 源IP地址欺骗式攻击 入侵者从外部传输一个伪装成来自内部主机的数据包 数据包的IP是 内网的合法IP 对策 丢弃所有来自路由器外端口 却使用内部源地址的数据包 1 2 源路由攻击 入侵者让数据包循着一个不可
  • 国外拨号VPS指南:开启你的全球网络之旅

    在当今数字化时代 互联网已经成为了我们生活的一部分 而要在全球范围内畅通无阻地访问互联网 拥有一个可靠的国外拨号VPS是非常重要的 无论您是为了工作 学习还是娱乐 国外拨号VPS都可以为您提供更广泛的网络体验 本文将为您提供国外拨号VPS的
  • MongoDB - 库、集合、文档(操作 + 演示 + 注意事项)

    目录 一 MongoDB 1 1 简介 a MongoDB 是什么 为什么要使用 MongoDB b 应用场景 c MongoDB 这么强大 是不是可以直接代替 MySQL d MongoDB 中的一些概念 e Docker 下载 1 2
  • 温室气体排放更敏感的模型(即更高的平衡气候敏感性(ECS))在数年到数十年时间尺度上也具有更高的温度变化(Python代码实现)

    欢迎来到本博客 博主优势 博客内容尽量做到思维缜密 逻辑清晰 为了方便读者 座右铭 行百里者 半于九十 本文目录如下 目录 1 概述 2 运行结果 3 参考文献 4 Python代码 数据
  • 为什么我强烈推荐大学生打CTF!

    前言 写这个文章是因为我很多粉丝都是学生 经常有人问 感觉大一第一个学期忙忙碌碌的过去了 啥都会一点 但是自己很难系统的学习到整个知识体系 很迷茫 想知道要如何高效学习 这篇文章我主要就围绕两点 减少那些罗里吧嗦的废话 直接上干货 CTF如
  • DSCA190V 57310001-PK

    DSCA190V 57310001 PK DSCA190V 57310001 PK 具有两个可编程继电器功能 并安装在坚固的 XP 外壳中 DSCA190V 57310001 PK 即可使用 只需最少的最终用户校准 DSCA190V 573

随机推荐

  • 编译 nccl-tests 项目

    1 编译 下载源代码 git clone recursive https github com NVIDIA nccl tests git 编译源代码 cd nccl tests make j 2 运行 cd build all reduc
  • 【MCU简单且容易理解的工作原理】

    SOC是啥 System on Chip 一个芯片 但是片上有好多东西的意思 市面上的AI芯片 包括你知道的麒麟xxx 骁龙xxx等等一些列手机芯片都是SOC 对于SOC设计者来讲 显然要知道码农们码出来的一行行代码是如何在SOC上跑起来的
  • uniapp微信小程序投票系统实战 (SpringBoot2+vue3.2+element plus ) -后端架构搭建

    锋哥原创的uniapp微信小程序投票系统实战 uniapp微信小程序投票系统实战课程 SpringBoot2 vue3 2 element plus 火爆连载更新中 哔哩哔哩 bilibili uniapp微信小程序投票系统实战课程 Spr
  • 租用服务器时要注意的事项有哪些

    租用 服务器 时要注意的事项 1 租用服务器时要选择可靠的机房线路 建议选择可靠的专业的BGP机房 确保在搭建网站的时候全网络覆盖 能够确保用户南北互通 2 租用服务器时要及时测试网络和硬件 建议先ping一下服务器的速度 或者测试一下下载
  • mayavi TypeError: unsupported operand type(s) for -: ‘str‘ and ‘str‘

    目录 mayavi安装报错 模拟异常 TypeError unsupported operand type s for str and str 模拟异常 TypeError unsupported operand type s for st
  • 题解 | #删除字符串中出现次数最少的字符# 利用map统计

    比预期的要低 HR打电话说是14级 不分ABC 说制造类供应链类工资和研发体系不一样 整体就要低一些 offer选择 大家帮忙看看 offer选择 大家帮忙看看 有奖活动 什么事是你实习了才知道的 春招会有好的国央企吗 招前端实习生 北京快
  • 判断完全数-第11届蓝桥杯省赛Python真题精选

    导读 超平老师的Scratch蓝桥杯真题解读系列在推出之后 受到了广大老师和家长的好评 非常感谢各位的认可和厚爱 作为回馈 超平老师计划推出 Python 蓝桥杯真题解析100讲 这是解读系列的第27讲 判断完全数 本题是2020年6月20
  • Python写入csv乱码

    把encoding utf 8 改成 encoding utf 8 sig 代码 import os csv with open result file mode a encoding utf 8 sig newline as f writ
  • 漏洞和网络扫描的相关知识

    学习 计算机网络安全 这本书的一些记录 一 漏洞的威胁 1 通过漏洞获取远程管理员权限 通常以管理员身份执行有缺陷的远程系统守护进程来获得远程管理员权限 这些漏洞的绝大部分来源于缓冲区溢出 少部分来自守护进程本身的逻辑缺陷 典型漏洞 Win
  • windows对微信及小程序抓包:Burp+Fiddler+Proxifier

    本文由掌控安全学院 zbs 投稿 话不多说 直接先上个效果图 新新的版本哈 好好的抓包哈 然后直接说我如何配置的 准备好三个工具 bp fiddler proxifier 也可以用其他的进行代理 bp proxifier正式课件有 fidd
  • 题解 | #删除字符串中出现次数最少的字符# 利用map统计

    比预期的要低 HR打电话说是14级 不分ABC 说制造类供应链类工资和研发体系不一样 整体就要低一些 offer选择 大家帮忙看看 offer选择 大家帮忙看看 有奖活动 什么事是你实习了才知道的 春招会有好的国央企吗 招前端实习生 北京快
  • CH341StreamI2C参数解释

    总体思路 第一步 使用EEPROM写入相应的数据 第二步 使用EEPROM读取相应的数据 第三步 使用CH341StreamI2C函数还原读取过程 每一步需要逻辑分析仪进行对比 第一步 数据写入CH341WriteEEPROM 使用CH34
  • 【SpringBoot】Java MVC 集成 Swagger 生成 API 文档

    使用Swagger你只需要按照它的规范去定义接口及接口相关的信息 就可以做到生成接口文档 以及在线接口调试页面 官网 https swagger io Knife4j 是为Java MVC框架集成Swagger生成Api文档的增强解决方案
  • 游戏策划:游戏开发中的关键环节

    在数字游戏的世界里 游戏策划是构建一个成功游戏的基石 游戏策划不仅仅是一个创意过程 它涉及从故事构建到技术实现的各个方面 以下是游戏策划中需要重点关注的几个重要内容 1 故事情节与世界观构建 一款游戏的魅力很大程度上取决于其故事情节和世界观
  • 内存取证volatility工具命令详解

    一 环境安装 1 kali下安装Volatility2 注意 一般Volatility2比Volatility3好用 wget https bootstrap pypa io pip 2 7 get pip py python2 get p
  • Qt undefined reference to `vtable for xxx‘

    一 问题背景 在编译QT代码时 出现 undefined reference to xxx entered 通过鼠标双击QtCreator 问题栏 中的该行 则会跳转到发送信号的代码所在行 与上述代码一同出现在 问题栏 的还有 undefi
  • 2023 全球网络黑客常用攻击方法 Top10_top10攻击

    近几年 借助互联网产业发展的东风 网络黑产也迎来更加巅峰的状态 不论是从攻击效率 组织规模 亦或是收益变现能力 都在一天天变的成熟完善 根据艾瑞咨询 2020 年发布的 现代网络诈骗分析报告 全国黑产从业者已经超过 40 万人 依托其从事网
  • prometheus grafana linux服务器监控

    文章目录 前传 node exporter安装 配置promethues监控node节点 grafana操作 查看监控 外传 前传 prometheus grafana的安装使用 https nanxiang blog csdn net a
  • 门店总数超9000家,手握大众茶饮“下沉市场牌”的古茗冲刺上市

    奶茶品牌上市潮来袭 1月2日 奶茶品牌古茗控股有限公司 下称 古茗 蜜雪冰城股份有限公司 下称 蜜雪冰城 一同递交招股书 计划在港交所主板上市 近年来 随着现制茶饮的爆火 赛道内主流玩家的资本化脚步也逐渐加快 2021年6月 奈雪的茶 HK
  • 【设计模式之美】理论一:怎么才算是单一原则、如何取舍单一原则

    文章目录 一 如何判断类的职责是否足够单一 二 类的职责是否设计得越单一越好 开始学习一些经典的设计原则 其中包括 SOLID KISS YAGNI DRY LOD 等 本文主要学习单一职责原则的相关内容 单一职责原则的定义 一个类只负责完