六大设计原则——单一职责原则【Single Responsibility Principle】
声明:本文内容是从网络书籍整理而来,并非原创。
用户管理的例子
- 先看一张用户管理的类图:
再看一眼上面的图,思考:这样合理吗?
这个接口是一个很糟糕的设计! 用户的属性和行为竟然混合在一起!!!
正确的做法是把用户的信息抽取成一个业务对象(Bussiness Object,简称 BO),把行为抽取成另外一个接口中,我们把这个类图重新画一下:
这样划分成了两个接口,IUserBO 负责用户的属性,IUserBiz 负责用户的行为,因为是面向的接口编程,所有当产生了这个 UserInfo 对象之后,既可以把它当 IUserBO 接口使用,也可以当 IUserBiz 接口使用,类似下面代码:
IUserBiz userInfo = new UserInfo(); //我要赋值了,我就认为它是一个纯粹的BO
IUserBO userBO = (IUserBO)userInfo;
userBO.setPassword("abc"); //我要执行动作了,我就认为是一个业务逻辑类
IUserBiz userBiz = (IUserBiz)userInfo;
userBiz.deleteUser();- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
问题解决了,但实际中我们更倾向于使用两个不同的类或接口, 一个就是IUserBO,一个是IUserBiz,如下图:
以上我们把一个接口拆分成两个接口的动作,就是依赖了单一职责原则,那什么是单一职责原则呢?
单一职责原则:应该有且仅有一个原因引起类的变更
SRP 的原话解释是:There should never be more than one reason for a class to change。
打电话的例子
电话通话的时候有四过程发生:拨号、通话、回应、挂机,那么看下接口图:
接口代码:public interface IPhone { //拨通电话
public void dial(String phoneNumber); //通话
public void chat(Object o); //回应,只有自己说话而没有回应,那算啥?!
public void answer(Object o); //通话完毕,挂电话
public void huangup();
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
想想,符合单一职责原则吗?
其实它有两个职责:一个是协议管理,一个是数据传送,diag()和 huangup()两个方法实现的是协议管理,拨号接通和关闭;chat()和answer()是数据的传送,把我们说的话转换成模拟信号或者是数字信号传递到对方,然后再把对方传递过来的信号还原成我们听的懂人话。这两个职责互不影响,所以考虑拆开,如下图:
这种设计完全满足类和接口的单一职责要求,但是一个手机类要把 ConnectionManager 和DataTransfer 组合在一块才能使用,组合是一种强耦合关系,都是有共同的生命期,这样的强耦合关系还不如使用接口实现的方式呢,而且还增加了类的复杂性,多了两个类呀,好,我们修改一下类图:
这样设计才为完美,一个手机实现了两个接口,把两个职责融合一个类中,你会觉得这个 Phone有两个原因引起变化了呀,是的是的,但是别忘记了我们是面向接口编程,我们对外公布的是接口而不是实现类;而且如果真要实现类的单一职责的话,这个就必须使用了上面组合的方式了,那这个会引起类间耦合过重的问题。
所以,对于接口,我们在设计的时候一定要做到单一,但是对于实现类就需要多方面考虑了,生搬硬套单一职责原则会引起类的剧增,给维护带来非常多的麻烦;而且过分的细分类的职责也会人为的制造系统的复杂性,本来一个类可以实现的行为非要拆成两个,然后使用聚合或组合的方式再耦合在一起,这个是人为制造了系统的复杂性,所以原则是死的,人是活的,这句话是非常好的。
单一职责的好处:
- 类的复杂性降低,实现什么职责都有清晰明确的定义;
- 可读性提高,复杂性降低,那当然可读性提高了;
- 可维护性提高,那当然了,可读性提高,那当然更容易维护了;
变更引起的风险降低,变更是必不可少的,接口的单一职责做的好的话,一个接口修改只对相应的实现类有影响,与其他的接口无影响,这个是对项目有非常大的帮助。
单一职责原则最难划分的就是职责,一个职责一个接口,但是问题是“职责”是一个没有量化的标准,一个类到底要负责那些职责?这些职责怎么细化?细化后是否都要有一个接口或类?这个都是需要从实际的项目区考虑的,从功能上来说,定义一个 IPhone 接口也没有错,实现了电话的功能呀,而且设计还很简单,就一个接口一个实现类,真正的项目我想大家一般都是会这么设计的,从设计原则上来看就有问题了,有两个可以变化的原因放到了一个接口中了,这就为以后的变化带来了风险,我从 2G 通讯协议修改到 3G 通讯,你看看你提供出的接口 IPhone 是不是要修改了?接口修改对其他的 Invoker 是不是有很大影响?!
方法的单一职责原则
单一职责使用于接口、类,同时也使用方法,什么意思呢?一个方法尽可能做一件事情,比如一个方法修改用户密码,别把这个方法放到“修改用户信息”方法中,这个方法的颗粒度很粗,比如这样一个方法:
在 IUserManager 中定义了一个方法叫 changeUser,根据传递的 type 不同,把可变长度参数changeOptions 修改到 userBo 这个对象上,并调用持久层的方法保存到数据库中。在我的项目组中如果有人写了这样一个方法,我不管他写了多上程序化了多少工夫,一律重写!原因是:方法职责不清晰,不单一,一般方法设计成这样的:
你要修改用户名称,就调用 changeUserName 方法,你要修改家庭地址就调用 changeHomeAddress,你要修改单位单户就调用 changeOfficeTel 方法,每个方法的职责就非常清晰,这也是一个良好的设计习惯。
所以,不管是对接口、类、方法使用了单一规则原则,那么快乐的就不仅仅是你了,还有你项目的成员,你的板,减少了因为变更引起的工作量呀,加官进爵等着你幺!
疑惑
你看到这里,就会问我,你写是类的设计原则吗?你通篇都在说接口的单一职责,类的单一职责你都违背了呀,呵呵,这个还真是的,我的本意是想把这个原则讲清楚,类的单一职责嘛,这个很简单,但当我回头写的时候,发觉才不是这么回事,翻看了以前一些设计和代码,基本上拿的出手的类设计都是和单一职责向违背的,静下心来回忆,发觉每一个类这样设计都是有原因的。这几天我查阅了 wikipedia、oodesign 等几个网站,专家和我也有类似的经验,基本上类的单一职责都用了类似的一句话来说“This is sometimes hard to see” ,这句话翻译过来就是“这个有时候很难说” ,是的,类的单一职责确实受非常多的因素制约,纯理论的来讲,这个原则是非常优秀的,但是现实有现实难处,你必须去考虑项目工期、成本、人员技术水平、硬件情况、网络情况甚至有时候还要考虑政府政策、垄断协议等等原因。
所以,对于单一职责原则,我的建议是接口一定要做到单一职责,类设计尽量只有一个原因引起变化。
版权声明:本文为博主原创文章,未经博主允许不得转载。
六大设计原则——单一职责原则【Single Responsibility Principle】的更多相关文章
- 面向对象设计原则 单一职责原则(Single responsibility principle)
单一职责原则(SRP:Single responsibility principle) 又称单一功能原则,面向对象的基本原则之一.它规定 一个类应该只有一个发生变化的原因. 该原则由罗伯特·C·马丁( ...
- 设计模式值六大原则——设计模式之六大原则——单一职责原则(SRP)
定义: 应该有且仅有一个原因引起类的变更. There should never be more than one reason for a class to change. 优点: 1.类的复杂性降 ...
- Java设计原则—单一职责原则(转)
定义: 应该有且仅有一个原因引起类的变更. There should never be more than one reason for a class to change. 优点: 1.类的复杂性降 ...
- 单一职责原则(Single Responsibility Principle)
单一职责原则(SRP:The Single Responsibility Principle) 一个类应该有且只有一个变化的原因. There should never be more than on ...
- 北风设计模式课程---单一职责原则(Single Responsibility Principle)
北风设计模式课程---单一职责原则(Single Responsibility Principle) 一.总结 一句话总结: 一个类应该有且只有一个变化的原因:单一职责原则(SRP:Single Re ...
- 设计模式之单一职责原则(SRP)
自己之前写过一些关于设计模式的博客,但是大部分都写得比较匆忙.现在正好趁年前有时间,笔者打算好好地整理一下自己这块知识结构.开篇的第一个原则就是设计原则里面最简单的一个原则--单一职责原则. 想必大家 ...
- 080 01 Android 零基础入门 02 Java面向对象 01 Java面向对象基础 01 初识面向对象 05 单一职责原则
080 01 Android 零基础入门 02 Java面向对象 01 Java面向对象基础 01 初识面向对象 05 单一职责原则 本文知识点:单一职责原则 说明:因为时间紧张,本人写博客过程中只是 ...
- IOS设计模式的六大设计原则之单一职责原则(SRP,Single Responsibility Principle)
定义 就一个类而言,应该仅有一个引起它变化的原因. 定义解读 这是六大原则中最简单的一种,通俗点说,就是不存在多个原因使得一个类发生变化,也就是一个类只负责一种职责的工作. 优点 类的复杂度降低,一个 ...
- 【设计原则和编程技巧】单一职责原则 (Single Responsibility Principle, SRP)
单一职责原则 (Single Responsibility Principle, SRP) 单一职责原则在设计模式中常被定义为“一个类应该只有一个发生变化的原因”,若我们有两个动机去改写一个方法,那这 ...
随机推荐
- jQuery $.fn.extend方式自定义插件
之前例子是扩展jQuery的工具方法,即通过$.xxx(para);的形式来使用的.下面是扩展jquery对象的方法,即任意一个jquery对象都已访问. 具体如下: wyl.js: (functio ...
- 修改SQL Server 排序规则 (未能排它地锁定数据库以执行该操作)
use master go alter database DBName set single_user go alter database DBName COLLATE Chinese_PRC_CI_ ...
- Python3 官方文档翻译 - 4.7 函数定义
4.7.1 默认函数定义 最常用的就是为一个或多个参数设定默认值,这让函数可以用比定义时更少的参数来调用,例如: def ask_ok(prompt, retries=4, complaint='Ye ...
- Buy Tickets(线段树)
Buy Tickets Time Limit: 4000MS Memory Limit: 65536K Total Submissions: 16607 Accepted: 8275 Desc ...
- Python重写C语言程序100例--Part9
''' [程序71] 题目:编写input()和output()函数输入,输出5个学生的数据记录. 1.程序分析: 2.程序源码: 使用list来模拟结构(不使用class) stu = [strin ...
- UIApplication相关
1,显示应用图标右上角的红色提示 application.applicationIconBadgeNumber = 10; 2.修改状态栏的类型 在当前控制器下设置 - (UIStatusBarSty ...
- Linux DM9000网卡驱动程序完全分析
Linux DM9000网卡驱动程序完全分析http://blog.csdn.net/ypoflyer/article/details/6209922
- BZOJ 4034: [HAOI2015]T2( 树链剖分 )
树链剖分...子树的树链剖分序必定是一段区间 , 先记录一下就好了 ------------------------------------------------------------------ ...
- centos6.5 mysql安装+远程访问+备份恢复+基本操作+卸载
参考博文: Linux学习之CentOS(十三)--CentOS6.4下Mysql数据库的安装与配置 MySQL修改root密码的多种方法 MySQL的备份与还原 解决mysql导入还原时乱码的问题 ...
- Swift 版本历史记录(关注)
http://numbbbbb.gitbooks.io/-the-swift-programming-language-/content/chapter1/03_revision_history.ht ...