设计模式六大原则之【单一职则原则】
一、什么是单一职责原则
首先, 我们来看单一职责的定义.

单一职责原则,全称Single Responsibility Principle, 简称SRP.
A class should have only one reason to change 类发生更改的原因应该只有一个

就一个类而言,应该仅有一个引起它变化的原因。应该只有一个职责。如果一个类有一个以上的职责,这些职责就耦合在了一起。一个职责的变化可能会削弱或者抑制这个类完成其他职责的能力。这会导致脆弱的设计。当一个职责发生变化时,可能会影响其它的职责。另外,多个职责耦合在一起,会影响复用性。想要避免这种现象的发生,就要尽可能的遵守单一职责原则。

单一职责原则的核心就是解耦和增强内聚性。
二、为什么要遵守单一职责原则?
通常 , 我们做事情都要知道为什么要这么做, 才回去做. 做的也有底气, 那么为什么我们要使用单一职责原则呢?

1、提高类的可维护性和可读写性
一个类的职责少了,复杂度降低了,代码就少了,可读性也就好了,可维护性自然就高了。

2、提高系统的可维护性
系统是由类组成的,每个类的可维护性高,相对来讲整个系统的可维护性就高。当然,前提是系统的架构没有问题。

3、降低变更的风险
一个类的职责越多,变更的可能性就越大,变更带来的风险也就越大
如果在一个类中可能会有多个发生变化的东西,这样的设计会带来风险, 我们尽量保证只有一个可以变化,其他变化的就放在其他类中,这样的好处就是 ** 提高内聚,降低耦合 **。

三. 单一职责原则应用的范围
单一职责原则适用的范围有接口、方法、类。按大家的说法,接口和方法必须保证单一职责,类就不必保证,只要符合业务就行。

3.1 【方法层面】单一职责原则的应用
现在有一个场景, 需要修改用户的用户名和密码. 就针对这个功能我们可以有多种实现.
第一种:

/*** 操作的类型*/
public enum OperateEnum {UPDATE_USERNAME,UPDATE_PASSWORD;
}public interface UserOperate {void updateUserInfo(OperateEnum type, UserInfo userInfo);
}public class UserOperateImpl implements UserOperate{@Overridepublic void updateUserInfo(OperateEnum type, UserInfo userInfo) {if (type == OperateEnum.UPDATE_PASSWORD) {// 修改密码} else if(type == OperateEnum.UPDATE_USERNAME) {// 修改用户名}}
}

第二种方法:

public interface UserOperate {void updateUserName(UserInfo userInfo);void updateUserPassword(UserInfo userInfo);
}public class UserOperateImpl implements UserOperate {@Overridepublic void updateUserName(UserInfo userInfo) {// 修改用户名逻辑}@Overridepublic void updateUserPassword(UserInfo userInfo) {// 修改密码逻辑}
}

来看看这两种实现的区别:

第一种实现是根据操作类型进行区分, 不同类型执行不同的逻辑. 把修改用户名和修改密码这两件事耦合在一起了. 如果客户端在操作的时候传错了类型, 那么就会发生错误.

第二种实现是我们推荐的实现方式. 修改用户名和修改密码逻辑分开. 各自执行各自的职责, 互不干扰. 功能清晰明了.
由此可见, 第二种设计是符合单一职责原则的. 这是在方法层面实现单一职责原则.

3.2 【接口层面】单一职责原则的应用
我们假设一个场景, 大家一起做家务, 张三扫地, 李四买菜. 李四买完菜回来还得做饭. 这个逻辑怎么实现呢?
方式一

/*** 做家务*/
public interface HouseWork {// 扫地void sweepFloor();// 购物void shopping();
}public class Zhangsan implements HouseWork{@Overridepublic void sweepFloor() {// 扫地}@Overridepublic void shopping() {}
}public class Lisi implements HouseWork{@Overridepublic void sweepFloor() {}@Overridepublic void shopping() {// 购物}
}

首先定义了一个做家务的接口, 定义两个方法扫地和买菜. 张三扫地, 就实现扫地接口. 李四买菜, 就实现买菜接口. 然后李四买完菜回来还要做饭, 于是就要在接口类中增加一个方法cooking. 张三和李四都重写这个方法, 但只有李四有具体实现.

这样设计本身就是不合理的.

首先: 张三只扫地, 但是他需要重写买菜方法, 李四不需要扫地, 但是李四也要重写扫地方法.

第二: 这也不符合开闭原则. 增加一种类型做饭, 要修改3个类. 这样当逻辑很复杂的时候, 很容易引起意外错误.

上面这种设计不符合单一职责原则, 修改一个地方, 影响了其他不需要修改的地方.
###方法二

/*** 做家务*/
public interface Hoursework {}public interface Shopping extends Hoursework{// 购物void shopping();
}public interface SweepFloor extends Hoursework{// 扫地void sweepFlooring();
}public class Zhangsan implements SweepFloor{@Overridepublic void sweepFlooring() {// 张三扫地}
}public class Lisi implements Shopping{@Overridepublic void shopping() {// 李四购物}
}

上面做家务不是定义成一个接口, 而是将扫地和做家务分开了. 张三扫地, 那么张三就实现扫地的接口. 李四购物, 李四就实现购物的接口. 后面李四要增加一个功能做饭. 那么就新增一个做饭接口, 这次只需要李四实现做饭接口就可以了.

public interface Cooking extends Hoursework{ void cooking();
}public class Lisi implements Shopping, Cooking{@Overridepublic void shopping() {// 李四购物}@Overridepublic void cooking() {// 李四做饭}
}

如上, 我们看到张三没有实现多余的接口, 李四也没有. 而且当新增功能的时候, 只影响了李四, 并没有影响张三.

这就是符合单一职责原则. 一个类只做一件事. 并且他的修改不会带来其他的变化.

3.3 【类层面】单一职责原则的应用
从类的层面来讲, 没有办法完全按照单一职责原来来拆分. 换种说法, 类的职责可大可小, 不想接口那样可以很明确的按照单一职责原则拆分. 只要符合逻辑有道理即可.

比如, 我们在网站首页可以注册, 登录, 微信登录.注册登录等操作. 我们通常的做法是:

public interface UserOperate {void login(UserInfo userInfo);void register(UserInfo userInfo);void logout(UserInfo userInfo);
}public class UserOperateImpl implements UserOperate{@Overridepublic void login(UserInfo userInfo) {// 用户登录}@Overridepublic void register(UserInfo userInfo) {// 用户注册}@Overridepublic void logout(UserInfo userInfo) {// 用户登出}
}

那如果按照单一职责原则拆分, 也可以拆分为下面的形式

public interface Register {void register();
}public interface Login {void login();
}public interface Logout {void logout();
}public class RegisterImpl implements Register{@Overridepublic void register() {}
}public class LoginImpl implements Login{@Overridepublic void login() {// 用户登录}
}public class LogoutImpl implements Logout{@Overridepublic void logout() {}
}

像上面这样写可不可以呢? 其实也可以, 就是类很多. 如果登录、注册、注销操作代码很多, 那么可以这么写.

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

例子:人的行为分析,包括了生活和工作等行为的分析,生活行为包括吃、跑、睡等行为,工作行为包括上下班,开会等行为,如下图所示:

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

4.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{@Overridepublic void eat() {System.out.println("吃饭");}@Overridepublic void running() {System.out.println("跑步");}@Overridepublic void sleeping() {System.out.println("睡觉");}
}

工作行为接口实现类:

public class WorkingBehaviorImpl implements WorkingBehavior{@Overridepublic void goToWork() {System.out.println("上班");}@Overridepublic void goOffWork() {System.out.println("下班");}@Overridepublic void meeting() {System.out.println("开会");}
}

第四步: 行为组合调用.
行为接口定义好了. 接下来会定义一个行为集合. 不同的用户拥有的行为是不一样 , 有的用户只用生活行为, 有的用户既有生活行为又有工作行为

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

  1. 行为组合接口BehaviorComposer
public interface BehaviorComposer {void add(IBehavior behavior);
}
  1. 行为组合接口实现类IBehaviorComposerImpl
public class IBehaviorComposerImpl implements BehaviorComposer {private List<IBehavior> behaviors = new ArrayList<>();@Overridepublic 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);}

可以看出单一职责的好处.
五、单一职责原则的优缺点

类的复杂性降低: 一个类实现什么职责都有清晰明确的定义了, 复杂性自然就降低了

可读性提高: 复杂性降低了,可读性自然就提高了

可维护性提高: 可读性提高了,代码就更容易维护了

变更引起的风险降低: 变更是必不可少的,如果接口的单一职责做得好,一个接口修改只对相应的实现类有影响,对其他的接口和类无影响,这对系统的扩展性、维护性都有非常大的帮助

设计模式六大原则(一)----单一职责原则相关推荐

  1. 学习设计模式 - 六大基本原则之单一职责原则

    设计模式总共有六大基本原则,统称为SOLID (稳定)原则,分别是S-单一职责原则(Single Responsibility Principle), O-开闭原则(Open closed Princ ...

  2. Java设计原则之单一职责原则、开闭原则、里氏代换原则

    文章目录 面向对象设计原则概述 单一职责原则 开闭原则 里氏代换原则 面向对象设计原则概述 软件的可维护性(Maintainability)和可复用性(Reusability)是两个非常重要的用于衡量 ...

  3. 经典设计原则:单一职责原则(SRP)

    本文详解设计原则中的单一职责原则,目的还是提高代码的可读性.可扩展性.复用性.可维护性等. 目录 1. 单一职责原则(SRP) 2. 如何理解单一职责原则? 3. 如何判断类的职责是否足够单一? 4. ...

  4. 面向对象的七种原则:单一职责原则,开放关闭原则

    我们的知识星球马上就要开始更新设计模式了,在更新设计模式之前,我们是不是需要做一些准备呢?否则设计模式中一些遵循的原则大家会一头雾水,所以我今天来给大家说一些面向对象的七种原则,有人说是6种有人说是7 ...

  5. SOLID原则:单一职责原则(SRP)

    SOLID:SOLID 原则并非单纯的1个原则,而是由5个设计原则组成,它们分别是:单一职责原则.开闭原则.里式替换原则.接口隔离原则和依赖反转原则,SOLID 由5个设计原则的头一个字母组成. 如何 ...

  6. 接口隔离原则和单一职责原则区别

    接口隔离原则和单一职责原则区别 单一职责原则是备受争议的原则,根据不同的业务逻辑,它会将系统功能模块划分成不同种类,产生多样的接口,同时每个接口尽量只包含一个功能(方法). 而产生争议的原因就是这个业 ...

  7. 【设计模式六大原则】:单一职责原则-带你走梦幻西游(一)

    依赖倒置原则(二) 开闭原则(三) 迪米特原则-带你走进梦幻西游(四) 里氏替换原则(五) 接口隔离原则(六) 定义       单一职责原则的英文名称是Single Responsibility P ...

  8. 架构中的设计原则之单一职责原则 - 《java开发技术-在架构中体验设计模式和算法之美》...

    2019独角兽企业重金招聘Python工程师标准>>> 单一职责模式: 单一职责原则的核心思想就是:系统中的每一个对象都应该只有一个单独的职责,而所有对象所关注的就是自身职责的完成. ...

  9. 设计模式之禅之单一职责原则

    声明:本文为阅读秦小波所写的<设计模式之禅>所写小结,文章内容可能有部分引述此书. 单一职责原则(Single Responsibility Principle) 1.定义:    在接口 ...

最新文章

  1. lisp提取长方形坐标_求修改lisp程序,如何提取CAD中多个点的坐标,(本人想提取UCS坐标系)另外只需要提取X,Y值,不要Z...
  2. Win7_Wifi热点
  3. Eclipse启动出现“Failed to create the Java Virtual Machine”错误
  4. 5-Interrupt Management Framework
  5. 图解算法学习笔记(目录)
  6. ECCV 2020 Spotlight 谷歌论文大盘点
  7. OSI参考模型(2)
  8. dentry path_lookat dput
  9. 【钉钉机器人 + 爬虫 + celery】定时发送微博热搜 + 定时发布财经新闻
  10. 2020-09-08风扇并联与串联应用学习
  11. 十大计算机恶意软件排行榜发布
  12. 关于解决netstat -nb中出现请求的操作需要提升问题
  13. [LOJ#6198]谢特[后缀数组+trie+并查集]
  14. java-URL短连接的生成(保证生成的唯一性)
  15. J2EE--自定义mvc增删改查
  16. 9139 位艺人在 Python 面前不值一提 # Python 爬虫小课 5-9
  17. 风拂树,月下杯影为伊留...
  18. 小工具,大作用:教你如何利用EXCEL函数LINEST做回归分析
  19. 什么是闭包,闭包是怎么产生的,闭包的应用在什么地方
  20. 【MATLAB教程案例12】基于GA遗传优化算法的函数极值计算matlab仿真及其他应用

热门文章

  1. haxe 第二期 入门篇 (转载)
  2. FPGA中关于复位的总结
  3. curl查询出口IP
  4. JBPM5 Spring
  5. JAVA基础讲义06-面向对象
  6. typedef 函数类型 详解
  7. MySQL常用命令小笔记
  8. tcpcopy mysql_利用tcpcopy引流过程
  9. Integrated Development
  10. C#+WPF 股票K线制作(包含时间/ 开盘价/ 最高价/最低价/ 收盘价 / 成交量)