六大设计原则——单一职责原则【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) 单一职责原则在设计模式中常被定义为“一个类应该只有一个发生变化的原因”,若我们有两个动机去改写一个方法,那这 ...
随机推荐
- (Problem 47)Distinct primes factors
The first two consecutive numbers to have two distinct prime factors are: 14 = 2 7 15 = 3 5 The fi ...
- c 占位符
%d, %i,代表整数,%f-浮点,%s,字符串,%c,char. %p 指针,%fL 长log,%e科学计数,%g 小数或科学计数. C语言中的格式占位符: %a,%A 读入一个浮点值(仅C9 ...
- 高级UNIX环境编程2
perror("error:") ; strerror 日历时间:time_t (1970.1.1开始的秒数) struct timeval (秒数和微秒) struc ...
- Mono for Android 显示远程图片
Main.axml <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:an ...
- Chromium如何显示Web页面
Displaying A Web Page In Chrome 概念化的应用分层 参见原文档:http://goo.gl/MsEJX 每一个box代表一个抽象层.下层不依赖于上层. WebKit:渲染 ...
- mysql ifnull if
IFNULL(expr1,expr2) 如果expr1不是NULL,IFNULL()返回expr1,否则它返回expr2.IFNULL()返回一个数字或字符串值,取决于它被使用的上下文环境. mysq ...
- spring jdbc 笔记3
spring JDBC 加入对commons-dbcp spring-jdbc spring-tx的依赖 1.数据源的配置 获取数据源在spring中的Bean管理默认已经是单例模式 关闭数据源d ...
- jquery-1.10.2.min.js之Multiple markers at this line
1.windows-preferences 输入validation 2. 点击进入 3. 将JavaScript validator for js files 的两个对勾去了就OK! 4 ...
- cocos2dx进阶学习之CCDirector
继承关系 CCDirecotor -> CCObject, TypeInfo 处理主窗口消息,管理何时.何种方式执行场景. 经常被翻译成导演,负责管理整个游戏的进程推动和周边支持. 成员 inl ...
- 最长回文串(manacher算法)
; ; int p[N]; char str[LEN], tmp[N]; //p[i]表示以str[i]为中心的回文往右延伸的 最长长度 void manacher(char* str, int* p ...