「设计模式」六大原则之一:单一职责小结

2023-05-16

文章目录

    • 1.单一职责原则定义
    • 2.如何理解单一职责原则(SRP)?
    • 3. 如何判断类的职责是否足够单一?
    • 4. 类的职责是否设计得越单一越好?
    • 5. 应用体现
    • 6. 应用示例1
    • 8 应用示例2(结合组合模式)
    • 9. 小结

「设计模式」六大原则系列链接:
「设计模式」六大原则之一:单一职责小结
「设计模式」六大原则之二:开闭职责小结
「设计模式」六大原则之三:里氏替换原则小结
「设计模式」六大原则之四:接口隔离原则小结
「设计模式」六大原则之五:依赖倒置原则小结
「设计模式」六大原则之六:最小知识原则小结

六大原则体现很多编程的底层逻辑:高内聚、低耦合、面向接口编程、面向抽象编程,最终实现可读、可复用、可维护性。

设计模式的六大原则有:

  • Single Responsibility Principle:单一职责原则
  • Open Closed Principle:开闭原则
  • Liskov Substitution Principle:里氏替换原则
  • Law of Demeter:迪米特法则(最少知道原则)
  • Interface Segregation Principle:接口隔离原则
  • Dependence Inversion Principle:依赖倒置原则
    把这六个原则的首字母联合起来( L 算做一个)就是 SOLID (solid,稳定的),其代表的含义就是这六个原则结合使用的好处:建立稳定、灵活、健壮的设计。

本文介绍 SOLID 中的第一个原则:单一职责原则。

1.单一职责原则定义

单一职责原则 [Single Responsibility Principle 简称 SRP],又叫单一功能原则。

A class or module should have a single responsibility。

我们把它翻译成中文,那就是:一个类或者模块只负责完成一个职责(或者功能)。

就一个类而言,应该仅有一个引起它变化的原因。

所谓职责就是指类变化的原因,也就是业务需求。如果有一个类有多于一个的原因被改变,那么这个类就有超过两个及以上的职责。而单一职责原则约定一个应该有且只有一个改变业类的原因。

单一职责原则的核心就是解耦和增强内聚性。

一句话总结:一个类只干一件事

应用实践:不要设计大而全的类,要设计粒度小、功能单一的类。换个角度来讲就是,一个类包含了两个或者两个以上业务不相干的功能,那我们就说它职责不够单一,应该将它拆分成多个功能更加单一、粒度更细的类。

2.如何理解单一职责原则(SRP)?

一个类只负责完成一个职责或者功能。不要设计大而全的类,要设计粒度小、功能单一的类。单一职责原则是为了实现代码高内聚、低耦合,提高代码的复用性、可读性、可维护性。

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

不同的应用场景、不同阶段的需求背景、不同的业务层面,对同一个类的职责是否单一,可能会有不同的判定结果。实际上,一些侧面的判断指标更具有指导意义和可执行性,比如,出现下面这些情况就有可能说明这类的设计不满足单一职责原则:

  • 类中的代码行数、函数或者属性过多;

    会影响代码的可读性和可维护性,我们就需要考虑对类进行拆分(也就是重构)。

    如果你是没有太多项目经验的编程初学者,实际上,我也可以给你一个凑活能用、比较宽泛的、可量化的标准,那就是一个类的代码行数最好不能超过 200 行,函数个数及属性个数都最好不要超过 10 个。

  • 类依赖的其他类过多,或者依赖类的其他类过多;

    不符合高内聚、低耦合的设计思想,我们就需要考虑对类进行拆分。

  • 私有方法过多;

    我们就要考虑能否将私有方法独立到新的类中,设置为 public 方法,供更多的类使用,从而提高代码的复用性。

  • 比较难给类起一个合适的名字;

    很难用一个业务名词概括,或者只能用一些笼统的 Manager、Context 之类的词语来命名,这就说明类的职责定义得可能不够清晰;

  • 类中大量的方法都是集中操作类中的某几个属性。

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

单一职责原则通过避免设计大而全的类,避免将不相关的功能耦合在一起,来提高类的内聚性。同时,类职责单一,类依赖的和被依赖的其他类也会变少,减少了代码的耦合性,以此来实现代码的高内聚、低耦合。但是,如果拆分得过细,实际上会适得其反,反倒会降低内聚性,也会影响代码的可维护性。

5. 应用体现

  • Android 中的架构演变思想运用体现:Android 里面 Activity 过于臃肿会让感觉很头大,MVP, MVVM等框架都是为了让 Activity 变得职责单一。
  • 架构分层,每一层要有明确的职责
  • 微服务
  • 模块
  • 通信协议
  • 接口等的设计
  • ……

6. 应用示例1

比如, 我们在网站首页可以登录、注册和退出登录等操作为例:

public interface UserOperate {

    void login(UserInfo userInfo);

    void register(UserInfo userInfo);

    void logout(UserInfo userInfo);
}


public class UserOperateImpl implements UserOperate{
    @Override
    public void login(UserInfo userInfo) {
        // 用户登录
    }

    @Override
    public void register(UserInfo userInfo) {
        // 用户注册
    }

    @Override
    public void logout(UserInfo userInfo) {
        // 用户退出
    }
}

那如果按照单一职责原则拆分, 拆分为如下形式:

public interface Register {
    void register();
}
public interface Login {
    void login();
}

public interface Logout {
    void logout();
}

public class RegisterImpl implements Register{

    @Override
    public void register() {
    }
}

public class LoginImpl implements Login{
    @Override
    public void login() {
    }
}

public class LogoutImpl implements Logout{

    @Override
    public void logout() {

    }
}

具体实践中,上面可以做为参考,是否拆分这种粒度,得结合具体业务来。也可以在项目初期写一个粗略的类,后面根据业务发展再拆分重构,平衡得自己根据业务来把控。

8 应用示例2(结合组合模式)

相同的职责放到一起,不同的职责分解到不同的接口和实现中去,这个是最容易也是最难运用的原则,关键还是要从业务出发,从需求出发,识别出同一种类型的职责。

人类的行为分成了两个接口:生活行为接口、工作行为接口,以及两个实现类。

如果都用一个实现类来承担这两个接口的职责,就会导致代码臃肿,不易维护,如果以后再加上其他行为,例如学习行为接口,将会产生变更风险(这里用到了组合模式)。

  • 定义一个行为接口

    /**
     * 行为包括两种: 生活、工作
     */
    public interface IBehavior {
        
    }
    

这里面定义了一个空的接口, 行为接口. 具体这个行为接口下面有哪些接口呢? 有生活和工作两方面的行为。

  • 定义生活和工作接口, 并且他们都是行为接口的子类

    生活行为接口:

    public interface LivingBehavior extends IBehavior{
        /** 吃饭 */
        void eat();
    
        /** 跑步 */
        void running();
    
        /** 睡觉 */
        void sleeping();
    }
    

    工作行为接口:

    public interface WorkingBehavior extends IBehavior{
        /** 上班 */
        void goToWork();
    
        /** 下班 */
        void goOffWork();
    
        /** 开会 */
        void meeting();
    }
    
  • 上述两个接口的实现类

    public class LivingBehaviorImpl implements LivingBehavior{
        @Override
        public void eat() {
            System.out.println("吃饭");
        }
    
        @Override
        public void running() {
            System.out.println("跑步");
        }
    
        @Override
        public void sleeping() {
            System.out.println("睡觉");
        }
    }
    public class WorkingBehaviorImpl implements WorkingBehavior{
    
        @Override
        public void goToWork() {
            System.out.println("上班");
        }
    
        @Override
        public void goOffWork() {
            System.out.println("下班");
        }
    
        @Override
        public void meeting() {
            System.out.println("开会");
        }
    }
    
  • 行为组合的调用

    接下来会定义一个行为集合. 不同的用户拥有的行为是不一样 , 有的用户只用生活行为, 有的用户既有生活行为又有工作行为。

    我们并不知道具体用户到底会有哪些行为, 所以,通常使用一个集合来接收用户的行为. 用户有哪些行为, 就往里面添加哪些行为。

    • 行为组合接口

      public interface BehaviorComposer {
          void add(IBehavior behavior);
      }
      
    • 实现组合接口

      public class IBehaviorComposerImpl implements BehaviorComposer {
      
          private List<IBehavior> behaviors = new ArrayList<>();
          @Override
          public void add(IBehavior behavior) {
              System.out.println("添加行为");
              behaviors.add(behavior);
          }
      
          public void doSomeThing() {
              behaviors.forEach(b->{
                  if(b instanceof LivingBehavior) {
                      LivingBehavior li = (LivingBehavior)b;
                      // 处理生活行为
                  } else if(b instanceof WorkingBehavior) {
                      WorkingBehavior wb = (WorkingBehavior) b;
                      // 处理工作行为
                  }
              });
          }
      }
      
  • 客户端调用

    public static void main(String[] args) {
            //  张三--全职妈妈 (只有生活行为)
            LivingBehavior zslivingBehavior = new LivingBehaviorImpl();
            BehaviorComposer zsBehaviorComposer = new IBehaviorComposerImpl();
            zsBehaviorComposer.add(zslivingBehavior);
    
            // 李四--职场妈妈 (生活、工作行为都有)
            LivingBehavior lsLivingBehavior = new LivingBehaviorImpl();
            WorkingBehavior lsWorkingBehavior = new WorkingBehaviorImpl();
    
            BehaviorComposer lsBehaviorComposer = new IBehaviorComposerImpl();
            lsBehaviorComposer.add(lsLivingBehavior);
            lsBehaviorComposer.add(lsWorkingBehavior);
        }
    

    示例中体现了单一职责的拆分与组合使用,仅参考。

9. 小结

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

参考:

设计模式之基-六大设计原则

单一职责原则

《设计模式之美》

《重学 Java 设计模式》

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

「设计模式」六大原则之一:单一职责小结 的相关文章

  • Manifest文件详解

    一 关于AndroidManifest xml AndroidManifest xml 是每个android程序中必须的文件 它位于整个项目的根目录 xff0c 描述了package中暴露的组件 xff08 activities servi
  • Android蓝牙完全学习手册

    1 前言 市面上关于Android的技术书籍很多 xff0c 几乎每本书也都会涉及到蓝牙开发 xff0c 但均是上层应用级别的 xff0c 而且篇幅也普遍短小 对于手机行业的开发者 xff0c 要进行蓝牙模块的维护 xff0c 就必须从An
  • 【高级】深入理解Word里的字号、行距、段距、间距、样式

    昨天领导交给我一份文档 xff0c 让我帮忙修改一下 改完后最后一页只有单独的一行 xff0c 打印出来不够美观 因此 xff0c 我缩小了行距 xff0c 把默认的单倍行距改为了固定值28磅 结果是 xff0c 整个文档的确少了一页 xf
  • 笔记本插上耳机后仍在外放Realtek Audio Console不支持此机器

    大年初七 xff0c 开工第一天 下午办公室新来的同事请教的如题问题 他用的华硕笔记本 xff0c 飞行堡垒FX86FE 插上华为耳机 xff0c 耳机始终播放不出来声音 显示已经检测到耳机插入了耳机孔 xff0c 点击弹窗会显示 Real
  • git Filename too long

    全局 git config global core longpaths true 当前仓库 git config core longpaths true 转载于 https www cnblogs com EasonJim p 108038
  • VxWorks入门级开发环境学习

    由于实习需要 xff0c 最近在学习VxWorks xff0c 久闻该操作系统大名 xff0c 一直被其深厚的内力震撼着从未敢去了解 xff0c 直到最近 操作系统Vxworks本身的优点特点等详细信息不多说了 xff0c 这里讲讲几天来我
  • 树莓派 Retropie 4.4中文版使用说明 含roms资源

    漫步云端服务器 http chdong top bbs http www chdong top 相关名词 Retropie Retropie可以将你的树莓派或者PC变成一台复古游戏机 Retropie基于完整的操作系统之上 xff0c 你可
  • selenium 中 css-寻找元素

    等同于 tag名 不改变 elements 61 wd find elements by css selector 39 div 39 elements 61 wd find elements by tag name 39 div 39 i
  • 解决 The following packages have unmet dependencies: 问题

    The following packages have unmet dependencies libvtk5 dev Depends libfreetype6 dev but it is not going to be installed
  • 2.1Ubuntu20.4安装QT5.14.2

    QT简介 xff1a Qt是一个跨平台的C 43 43 图形用户界面库 xff0c 我们平时所说所使用的Qt xff0c 准确的来说是它的GUI编程部分 Qt提供给应用程序开发者建立图形用户界面所需要的功能 xff0c 并且Qt很容易扩展
  • 美国出台最严技术出口管制!14项前沿科技面临封锁

    关注ITValue xff0c 查看企业级市场最新鲜 最具价值的报道 xff01 xff08 本文转载自量子位公众号 xff0c ID xff1a QbitAI xff0c 作者 xff1a 乾明 夏乙 问耕 xff09 美国又打出一套七伤
  • sftp文件上传详解

    JSch是Java Secure Channel的缩写 JSch是一个SSH2的纯Java实现 它允许你连接到一个SSH服务器 xff0c 并且可以使用端口转发 xff0c X11转发 xff0c 文件传输等 xff0c 当然你也可以集成它
  • 数据库设计 ER图

    一 E R图构成要素 E R图也称实体 联系图 Entity Relationship Diagram xff0c 提供了表示实体类型 属性和联系的方法 xff0c 用来描述现实世界的概念模型 它是描述现实世界关系概念模型的有效方法 是表示
  • ssh-keygen -t rsa详解

    ssh keygen q 安静模式 b bits 位数 t dsa ecdsa ed25519 rsa rsa1 加密算法 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 6
  • samba源码编译安装(版本4.13.0)

    目录 前言编译安装samba下载三个包安装依赖共需要安装samba gnutls nettle1 进入解压后的samba目录执行 xff1a 2 进入解压后的gnutls目录执行3 进入解压后的nettle目录执行3 1 卸载已存在nett
  • mybatis+postgresql10插入返回主键ID

    MyBatis如果使用useGeneratedKeys去生成自增列会造成不成功 xff0c 因为官方提供只支持这些数据库 xff1a mybatis generatedkeys xff0c 那么如果要用在oracle和postgresql上
  • Mac 环境下使用 homebrew 安装和彻底卸载 MySQL5.7

    一 安装 MySQL5 7 在终端执行下列命令 xff1a brew install mysql 64 5 7 使用 brew 安装好 MySQL 5 7 版本后 xff0c 此时 MySQL root 用户的密码是空的 我们需要修改一下
  • python 爬取网站首页并获取资源文件

    一 使用requests模块 xff0c 如果没有安装请使用如下命令 xff0c 安装requests模块 pip install requests 二 打开PyCharm xff0c 创建一个新的py文件 1 请求网站 xff0c 获取网
  • github pages部署静态网页

    github pages部署静态网页 如果你的项目只是一个静态网站 xff0c 就没有必要再去整什么服务器 xff0c github pages 提供了搭建静态网站的功能 xff1b 为什么使用Github Pages 1 搭建简单而且免费
  • 使用 @PostConstruct、@Bean(initMethod=“”)注解指定初始化方法 、及实现InitializingBean接口 在 Spring Bean 完成初始化后进行一些响应的操作

    如果需要在 某个特定的bean 被初始化后 xff0c 执行一些代码逻辑 xff0c 可以使用 64 PostConstruct 64 Bean xff08 initMethod 61 xff09 注解指定初始化方法 及实现Initiali

随机推荐

  • RK3399环境搭建----glibc2.29编译

    硬件 xff1a firefly RK3399 JD4 软件 xff1a ubuntu18 04 需求 xff1a 在RK3399开发板上进行在线调试 调试需要用到GDB xff0c 之前拿到的gdb运行的时候报错 xff1a 提示libm
  • Sql Server索引

    1 创建索引时遵循一般原则 在需要经常搜索的列上面创建索引 在主键上创建索引 在外键上创建索引 在经常用于Where子句的列上创建索引 在经常需要排序的列上创建索引 对于数据类型为text xff0c image xff0c bit的列不应
  • 迅雷下载提示:应版权方要求/包含违规内容, 文件无法下载

    问题 在使用迅雷下载资源时 xff0c 可能会提示应版权方要求 xff0c 无法下载或包含违规内容 xff0c 无法下载 xff0c 如下图 很多人可能此时已经放弃了 xff0c 不过 xff0c 迅雷可以配合Free Download M
  • 有趣的教程:在PyCharm2019.2版本中集成Jupyter Notebooks!

    PyCharm是一种Python IDE xff0c 其带有一整套可以帮助用户在使用Python语言开发时提高其效率的工具 此外 xff0c 该IDE提供了一些高级功能 xff0c 以用于Django框架下的专业Web开发 嗨 xff0c
  • 分享文件的另一种选择--Firefox Send

    提起 Firefox xff0c 大家熟知的应该就是 Firefox Browser xff0c 即 火狐浏览器 xff0c 但是今天要说的是火狐旗下的文件分享产品 Firefox Send xff0c 可以让你安全地进行文件分享 适用场景
  • 消除if...else的十种方法

    前言 if else 是所有高级编程语言都有的必备功能 但现实中的代码往往存在着过多的 if else 虽然 if else 是必须的 xff0c 但滥用 if else 会对代码的可读性 可维护性造成很大伤害 xff0c 进而危害到整个软
  • Qt学习笔记(三)运行库依赖

    正常应该写release版本的依赖库 xff0c 不过都差不多 xff0c 自行转化就对了 自己使用qt版本 Qt5 15 2 xff0c 因为我只编译了debug版本程序 xff0c 暂时只能拿debug说事了 直接查找依赖可以找到依赖有
  • 宽带测速网站收集

    国际通用 xff1a https www speedtest net http www speedtest cn xff08 国产 xff0c 主要服务器节点多 xff09 电信 xff1a https 10000 gd cn 移动 xff
  • 使用Git为每个项目配置独立的用户名和邮箱

    使用Git为每个项目配置独立的用户名和邮箱 平时在开发的过程中 xff0c 用到git时 xff0c 要做一些特殊的配置 xff0c 例如自己公司内网https的SSL证书未经过第三方机构签署 xff0c 直接操作Git就会报错 xff0c
  • 电脑重装系统后文件还能恢复吗?2个方法助你还原文件

    电脑重装系统后文件还能恢复吗 xff1f 此时可能你面临一个难题 xff1a 已经重装了电脑 xff0c 但是发现丢失的问题 实际上 xff0c 一般当电脑出现运行缓慢 xff0c 或者是电脑系统出现故障无法修复的时候 xff0c 大家都会
  • C盘重装还能恢复以前文件嘛,重装C盘数据备份清单

    重装系统主要是对C盘的重装 xff0c 也就是对C盘的格式化 xff0c 电脑安装系统的位置是C盘 xff0c 常见的重装方法只影响C盘而不会丢失其他盘的数据 那么C盘重装还能恢复以前的文件嘛 xff1f 重装C盘时什么文件必须备份呢 xf
  • HTML Parsing Error:Unable to modify the parent container element before the child element is closed

    一 又涨见识了 IE8报下面错误 xff0c 而且是我的机器不报 xff0c 同事的机器报 xff0c 试了4台 xff0c 两个报 xff0c 两个不报 xff0c IE版本都一样 xff0c 没想明白 解决 xff1a 1 查看是否有未
  • 架构师必须学会的几种 UML图

    文章目录 一 UML 是什么定义作用 二 UML 画图的工具推荐三 UML 类图中的关系1 泛化 继承 2 实现 xff08 Realization xff09 3 关联 xff08 Association 4 聚合 xff08 Aggre
  • 算法:如何实现两个大数相加

    文章目录 问题要求思路代码实现 问题 实现两个很大很大的数相加 xff0c 求出它们的和 要求 1 是整数 xff1b 2 两个数无限大 xff0c long 都装不下 xff1b 3 不能用 BigInteger xff1b 4 不能用任
  • 关于Android 未来开发的方向探讨

    文章目录 背景Anroid 应用开发工程师技能参考Android 基础设施建设技能参考Android 逆向开发工程师技能参考Android Framework工程师技能参考 xff08 车载系统 系统开发方向 xff09 Android 性
  • Git Commit Message 规范实践

    文章目录 背景示例FeatureBugRefactorDocsTestStyleChore 每个Merge Request只有一个Commit小结附录 背景 本文总结了项目实践中的 Commit Message 规范 任何一笔提交都是有原因
  • github push 报错的问题记录

    文章目录 背景解决办法示例 背景 当 git 执行 git push xff08 origin origin master xff09 后出现错误提示 xff1a git push origin origin master Password
  • Mac 平台 Android FFmpeg 编译与集成实践

    文章目录 FFmpeg 是什么为什么要学 FFmpeg 开发FFmpeg 编译1 编译环境准备2 FFmpeg 环境配置3 完整的脚本4 FQA 编译小结在Android Studio 中的集成集成问题记录写在最后 FFmpeg 是什么 F
  • 音频中文件大小计算公式(转)

    声卡对声音的处理质量可以用三个基本参数来衡量 xff0c 即采样频率 采样位数和声道数 采样频率是指单位时间内的采样次数 采样频率越大 xff0c 采样点之间的间隔就越小 xff0c 数字化后得到的声音就越逼真 xff0c 但相应的数据量就
  • 「设计模式」六大原则之一:单一职责小结

    文章目录 1 单一职责原则定义2 如何理解单一职责原则 xff08 SRP xff09 xff1f 3 如何判断类的职责是否足够单一 xff1f 4 类的职责是否设计得越单一越好 xff1f 5 应用体现6 应用示例18 应用示例2 xff