单一职责原则经典例子.docx

上传人:b****8 文档编号:23644112 上传时间:2023-05-19 格式:DOCX 页数:12 大小:23.46KB
下载 相关 举报
单一职责原则经典例子.docx_第1页
第1页 / 共12页
单一职责原则经典例子.docx_第2页
第2页 / 共12页
单一职责原则经典例子.docx_第3页
第3页 / 共12页
单一职责原则经典例子.docx_第4页
第4页 / 共12页
单一职责原则经典例子.docx_第5页
第5页 / 共12页
点击查看更多>>
下载资源
资源描述

单一职责原则经典例子.docx

《单一职责原则经典例子.docx》由会员分享,可在线阅读,更多相关《单一职责原则经典例子.docx(12页珍藏版)》请在冰豆网上搜索。

单一职责原则经典例子.docx

单一职责原则经典例子

单一职责原则经典例子

【篇一:

单一职责原则经典例子】

定义:

thereshouldneverbemorethanonereasonforaclasstochange,应该有且仅有一个原因引起类的变更。

职责:

业务逻辑,或者对象能够承担的责任,并以某种行为方式来执行。

2.理解

该原则提出了对对象职责的一种理想期望。

对象不应该承担太多职责,正如人不应该一心分为二用。

唯有专注,才能保证对象的高内聚;唯有单一,才能保证对象的细粒度。

对象的高内聚与细粒度有利于对象的重用。

一个庞大的对象承担了太多的职责,当客户端需要该对象的某一个职责时,就不得不将所有的职责都包含进来,从而造成冗余代码或代码的浪费。

这实际上保证了dry原则,即不要重复你自己(dontrepeatyourself),确保系统中的每项知识或功能都只在一个地方描述或实现。

单一职责原则还有利于对象的稳定。

所谓职责,就是对象能够承担的责任,并以某种行为方式来执行。

对象的职责总是要提供给其他对象调用,从而形成对象与对象的协作,由此产生对象之间的依赖关系。

对象的职责越少,则对象之间的依赖关系就越少,耦合度减弱,受其他对象的约束与牵制就越少,从而保证了系统的可扩展性。

单一职责原则并不是极端地要求我们只能为对象定义一个职责,而是利用极端的表述方式重点强调,在定义对象职责时,必须考虑职责与对象之间的所属关系。

职责必须恰如其分地表现对象的行为,而不至于破坏和谐与平衡的美感,甚至不入。

换言之,该原则描述的单一职责指的是公开在外的与该对象紧密相关的一组职责。

例如,在媒体播放器中,可以在mediaplayer类中定义一组与媒体播放相关的方法,如open()、play()、stop()等。

这些方法从职责的角度来讲,是内聚的,完全符合单一职责原则中专注于做一件事的要求。

如果需求发生扩充,需要我们提供上传、下载媒体文件的功能。

那么在设计时,就应该定义一个新类如mediatransfer,由它来承担这一职责;而不是为了方便,草率地将其添加到mediaplayer类中。

单一职责适用于接口、类、同时也适用于方法。

方法的粒度也不宜过粗。

3.问题由来

类T负责两个不事的职责:

职责P1、职责P2。

当由于职责P1需求发生改变而需要修改类T时,有可能会导致原来运行的职责P2功能发生故障。

解决方法:

分别建立两个类完成对应的功能。

[待补充]

4.好处:

类的复杂性降低,实现什么职责都有清晰明确的定义;可读性提高,复杂性降低,那当然可读性提高了;可维护性提高,那当然了,可读性提高,那当然更容易维护了;变更引起的风险降低,变更是必不可少的,接口的单一职责做的好的话,一个接口修改只对相应的实现类有影响,与其他的接口无影响,这个是对项目有非常大的帮助。

5.难点

5.1职责划分无量化标准:

学究理论还是工程应用?

后者时,要考虑可变因素与不可变因素,以及相关的收益成本比率等。

5.2单一职责妥协:

项目中单一职责原则很少得以体现,或者非常难(囿于国内技术人员的地位、话语权、项目中的环境、工作量、人员的技术水平、硬件资源等,最终的结果就是常常违背单一职责原则)。

6.实践建议

6.1接口一定要做到SRP,类的设计尽量做到只有一个原因引起变化。

6.2妥协原则:

A.只有逻辑足够简单,才可以在代码级别上违背SRP;

B.只有类中方法数量足够少,才可以在方法级别上违背SRP;

C.实际应用中的类都要复杂的多,一旦发生职责扩散而需要修改类时,除非这个类本身非常简单,否则还是要遵循SRP。

7.范例

7.1职责分明示例(role-basedaccesscontrol,基于角色的访问控制)(属性与行为分离)

项目中常用到用户、机构、角色管理这些模块,基本上使用的都是RBAC模型(通过分配和取消角色来完成用户权限的授予与取消,使动作主体(用户)与资源的行为(权限)分离)。

但上述接口设计得有问题,用户的属性与用户的行为没有分开。

应将其拆分为两个接口,iuserbo负责用户的属性,也即收集和反馈用户的属性信息;iuserbiz负责用户的行为,完成用户信息的维护与变更。

代码清单1-1分清职责后的代码示例

.......iuserbizuserinfo=newuserinfo();//我要赋值了,我就认为它是一个纯粹的boiuserbouserbo=(iuserbo)userinfo;userbo.setpassword(abc//我要执行动作了,我就认为是一个业务逻辑类iuserbizuserbiz=(iuserbiz)userinfo;userbiz.deleteuser();.......

实际上,在项目中,更倾向于使用如下的结构图:

7.2职责分明的电话类图(行为与行为相分离)

举个电话的例子,电话通话的时候有4个过程发生:

拔号、通话、回应、挂机,写一个接口,其类图如下:

publicinterfaceiphone{//拨通电话publicvoiddial(stringphonenumber);//通话publicvoidchat(objecto);//回应,只有自己说话而没有回应,那算啥?

publicvoidanswer(objecto);//通话完毕,挂电话publicvoidhuangup();}iphone这个接口包含了两个职责:

一个是协议管理,由dial()与hangup()两个方法实现;一个是数据传输,由chat()与answer()实现。

考虑:

A.协议接通的变化会引起这个接口或实现类的变化吗?

会的!

数据传输(电话不仅仅通话,还可以上网)的变化也会引起其变化!

这两个原因都引起了类的变化。

B.电话拔通还用管使用什么协议吗?

电话连接后还需要关心传递什么数据吗?

都不,即这两个职责的变化不相互影响,那就考虑拆分成两个接口,类图如下:

这个类图略有些复杂,一个手机类需要把两个对象组合在一起才能用。

组合是一种强耦合关系,还不如使用接口实现的方式呢。

修改如下:

7.3职责分明到接口

修改用户信息方法----〉职责分明的方法

上述写法很糟(职责不清晰,不单一),改了吧。

7.4分层架构模式(一个较大的单一职责示例)

分层架构模式实际上也体现了这一原则,它将整个系统按照职责的内聚性分为不同的层,层内的模块与类具有宏观的内聚性,它们关注的事情应该是一致的。

例如,领域逻辑层就主要关注系统的业务逻辑与业务流程,而数据的持久化与访问则交由数据访问层来负责。

以订单的管理为例,我们在领域逻辑层中定义如下的类ordermanager:

publicclassordermanagerprivateiorderrepositoryrepository=repositoryfactory.createorderrepository();publicvoidplace(orderorder)if(order.isvalid())repository.add(order);elsethrownewinvalidoperationexception(ordercantbeplaced.publicvoidcancel(orderorder)if(order.isvalid()order.cancancel(datetime.now))repository.remove(order);elsethrownewinvalidoperationexception(ordercantbecanceled.publicstaticclassrepositoryfactorypublicstaticiorderrepositorycreateorderrepository()returnneworderrepository();

ordermanager类的实现体现了单一职责原则的思想。

首先,ordermanager类中的place()和cancel()方法均属于订单管理的业务逻辑,与领域逻辑层关注的事情是一致的。

在这两个方法的实现中,我们需要检验订单的正确性(检验订单是否包含了必要的信息,如联系人、联系地址与联系电话),以及判断当前时间是否在允许取消订单的时间范围内。

虽然它们仍然属于订单处理的业务逻辑,但拥有这些检查信息的是order对象,而不是ordermanager,即order对象是检查订单的信息专家。

因此,isvalid()和cancancel()方法应该被定义在order类中。

至于添加和移除订单的操作,虽然保证了下订单和取消订单的业务逻辑实现,但其实现却属于数据访问层的范畴,因而该职责被委派给了orderrepository类。

至于repositoryfactory类,则是负责创建orderrepository对象的工厂类。

这些类的职责以及协作关系如图2-4所示。

将数据访问的逻辑从领域对象中分离出去是有道理的,因为数据访问逻辑的变化方向与订单业务逻辑的变化方向是不一致的,引起职责发生变化的原因也不相同。

这也是单一职责原则的核心思想。

遵循该原则,就能够有效地分离对象的变与不变,将变化的职责以抽象的方式独立于原对象之外,原对象就更加稳定。

martinfowler认为,设计一个模型时应使该模型中最频繁修改的部分所影响的类型数量达到最少。

我们对访问order数据表的逻辑进行了封装与抽象,以隔离数据访问逻辑的变化,即使数据访问逻辑发生变化,它影响到的只是orderrepository类而已。

5妥协示例(项目中常见的单一职责违背可接受示例)所谓职责扩散,就是因为某种原因,职责P被分化为粒度更细的职责P1和P2.

比职:

类T只负责一个职责P,这样设计是符合SRP的。

后来由于某种原因,需要将职责P细分为粒度更细的P1与P2,这时如果要遵循SRP,需要将类T也分解为两个类T1和T2,分别负责P1、P2这两个职责。

但是在程序已经写好的情况下,这样做简直太费时间了。

所以,简单的修改类T,用它来负责两个职责是一个比较不错的选择,虽然这样做有悖于SRP。

如,用一个类描述动物呼吸这个场景:

classanimal{publicvoidbreathe(stringanimal){system.out.println(animal+呼吸空气publicclassclient{publicstaticvoidmain(string[]args){animalanimal=newanimal();animal.breathe(羊运行结果:

羊呼吸空气

程序上线后,发现问题了,并不所有动物都呼吸空气的,如鱼是呼吸水的。

修改时如若遵循SRP,则需将animal类细分为陆生动物类terrestrial,水生动物aquatic,代码如下:

[修改方式一]classterrestrial{publicvoidbreathe(stringanimal){system.out.println(animal+呼吸空气classaquatic{publicvoidbreathe(stringanimal){system.out.println(animal+呼吸水publicclassclient{publicstaticvoidmain(string[]args){terrestrialterrestrial=newterrestrial();terrestrial.breathe(羊aquaticaquatic=newaquatic();aquatic.breathe(鱼运行结果:

羊呼吸空气鱼呼吸水

我们发现这样修改花销是很大的,除了将原来的类分解之外,还需要修改客户端。

而直接修改类animal来达成目的,虽然违背SRP,但花销却小的多,代码如下:

[修改方式二]classanimal{publicvoidbreathe(stringanimal){if(鱼.equals(animal)){system.out.println(animal+呼吸水}else{system.out.println(animal+呼吸空气publicclassclient{publicstaticvoidmain(string[]args){animalanimal=newanimal();animal.breathe(羊animal.breathe(鱼运行结果:

羊呼吸空气鱼呼吸水

可以看到,这种修改方式要简单得多。

但是却存在着隐患:

有一天需要将鱼分为呼吸淡水和海水的鱼,则又要修改animal类的breathe方法,而对原有的代码修改会对调用“羊”等相关功能带来风险,也许某一天,你会发现程序运行的结果变为“羊呼吸水”了。

这种修改方式直接在代码级别上违背了SRP,虽然修改起来最简单,但隐患却最大。

还有一种修改方式:

[修改方式三]classanimal{publicvoidbreathe(stringanimal){system.out.println(animal+呼吸水publicvoidbreathe2(stringanimal){system.out.println(animal+呼吸空气publicclassclient{publicstaticvoidmain(string[]args){animalanimal=newanimal();animal.breathe(羊animal.breathe2(鱼运行结果:

羊呼吸空气鱼呼吸水

可以看出,这种修改没有改动原来的方法,而是在类中添加了一个方法,这样虽然也违背了SRP,但在方法级别上却是符合SRP的,因为它并没有改动原来方法的代码。

这三种方式各有优缺点,那么在实际编程中,采用哪一种呢?

这需要根据实际情况而定:

建议:

A.只有逻辑足够简单,才可以在代码级别上违背SRP;

B.只有类中方法数量足够少,才可以在方法级别上违背SRP;

例如本文所举的这个例子,它太简单,它只有一个方法,所以,无论在代码还是在方法级别违背SRP,都不会造成太大的影响。

实际应用中的类都要复杂的多,一旦发生职责扩散而需要修改类时,除非这个类本身非常简单,否则还是要遵循SRP。

8.找出两个认为拿得出手的类。

【篇二:

单一职责原则经典例子】

在面向对象设计中,对于单一职责原则(srp:

singleresponsibilityprinciple),相信开发人员都耳熟能详,大多数人也能回答什么是单一职责原则。

但是追问到为什么要单一职责以及它带来的好处是什么,估计少部分人就很难分析清楚了。

同时,基本哲学原理告诉我们,事物都有两面性。

那么,如果很好地实践了单一职责,会带来什么负面影响吗?

这些都是我曾经不明白的地方,现在稍微明白了一些,今天聊聊我对这些问题的认识。

也许还不够深入和全面,还需要继续在以后的工作中不断实践,深化理解。

srp这一概念,由robotc.martin在其广泛流行的经典著作《agilesoftwaredevelopment,principles,patterns,andpractices》中的面向对象设计原则部分引入。

此原则有两个约束:

职责:

什么是职责?

我之前认为是一个类要实现的功能,或者提供的服务,从面向对象的角度说是内聚性。

从这个角度去理解也可以,但是,功能的大小怎么定义是个难题,什么样的功能放在一起是好的内聚也不好定义。

我们知道,面向对象设计更多考虑变化因素可能导致系统的改变,从而提供更灵活应变的设计。

所以robertc.martin从变化这个角度对职责的定义为:

“变化的原因(areasonforchange)”,理解为,如果你能想到多于一个的动机去改变一个类,那么这个类就具有多于一个的职责。

单一:

只有一个引起类改变的原因。

在martin的书中分析单一职责带来的好处是,可以分离变化,减少改动,减少不必要的依赖,从而在发生变化时减少设计所遭受的破坏。

但更多的是从类和接口的角度去分析。

然而srp对每一个程序模块都是适用的,比如小到代码块,函数,大到包级别,组件级别。

举例谈谈函数的srp,除了以上提到的非srp带来的缺点之外,还有什么呢?

请看下面一段java代码:

12345678910publicstringsetdatelabelbyname(stringname,datedate){

mapstring,stringlabels=getcurrentdatelabels();

simpledateformatsimpledateformat=newsimpledateformat(“yyyy-mm-dd”);

stringformatteddate=simpledateformat.format(date);

if(labels.containskey(name)){

labels.put(name,formatteddate);

return“success”;

}

return“namenotexists”;

}

这段代码是我臆想出来的,但是我再现实中也看到不少类似的代码,让咱们一起来看看它里面的坏味道:

从函数签名看来,此函数的目的是通过一个名字设置一个日期标志。

但是在设置的同时有返回值,一般看来这个函数承担了query和command两个功能;

从函数实现看来,其中做了日期格式化功能;

从返回值看来,将返回的字符串写死,势必会将使用此函数的对象紧密耦合。

如此可见,引起此函数变化的可能因素有:

日期format格式的改变;

返回值的定义改变,比如成功不像返回“success”或者需要增加新的错误类型;

如果对此函数重构,可以抽取一个新类来提供format服务,比如叫dateutil,同时对返回值利用异常处理错误情况。

1234567891011publicclassdateutil{

publicstaticstringformat(datedate){

simpledateformatsimpledateformat=newsimpledateformat(“yyyy-mm-dd”);

returnsimpledateformat.format(date);

}

}

在此我不想着重谈重构,但是基于以上的分析可以看出,至少做到单一职责的好处有:

提供重用基础:

代码块独立,功能单一,可以容易地重用

提高可读性:

函数实现代码行数变少,利用函数名说明功能,不关心实现细节

提供可扩展性:

依赖别的单元提供的接口,便于扩展

我想其他软件构成单元:

包、组件、框架、库都有类似的道理。

srp是一把双刃剑,如果做到极端,没有合理的权衡,对于类来说就有可能形成爆炸。

类爆炸的可能性有,职责没有划分清楚而形成过多的类;因为业务的需要,需求的复杂性带来的。

不论其产生的原因,过多的类,如果没有好的组织方式,杂乱无章,势必带来维护的复杂性。

所谓维护的复杂性,在于代码的可读性,以及重构的高成本。

一种可行的解决方案就是利用模式来管理,定义清晰的层次结构。

比如利用工厂,模板方法等等。

总结起来,对于srp,正如robertc.martin所说,是一起面向对象设计中最简单的原则之一,也是最难正确应用之一。

它是其它原则的基础,比如ocp(openforextension,closeformodification)。

但是,万变不离其宗,本质是抽象变化,解除耦合,让设计出来的系统可读性好,可维护性好,可扩展性好。

合理的职责划定带来清晰代码单元结构,如果设计不合理则适得其反,导致代码难以读懂,维护成本增加。

原文链接:

【篇三:

单一职责原则经典例子】

定义:

不要存在多于一个导致类变更的原因。

通俗的说,即一个类只负责一项职责。

问题由来:

类t负责两个不同的职责:

职责p1,职责p2。

当由于职责p1需求发生改变而需要修改类t时,有可能会导致原本运行正常的职责p2功能发生故障。

解决方案:

遵循单一职责原则。

分别建立两个类t1、t2,使t1完成职责p1功能,t2完成职责p2功能。

这样,当修改类t1时,不会使职责p2发生故障风险;同理,当修改t2时,也不会使职责p1发生故障风险。

说到单一职责原则,很多人都会不屑一顾。

因为它太简单了。

稍有经验的程序员即使从来没有读过设计模式、从来没有听说过单一职责原则,在设计软件时也会自觉的遵守这一重要原则,因为这是常识。

在软件编程中,谁也不希望因为修改了一个功能导致其他的功能发生故障。

而避免出现这一问题的方法便是遵循单一职责原则。

虽然单一职责原则如此简单,并且被认为是常识,但是即便是经验丰富的程序员写出的程序,也会有违背这一原则的代码存在。

为什么会出现这种现象呢?

因为有职责扩散。

所谓职责扩散,就是因为某种原因,职责p被分化为粒度更细的职责p1和p2。

比如:

类t只负责一个职责p,这样设计是符合单一职责原则的。

后来由于某种原因,也许是需求变更了,也许是程序的设计者境界提高了,需要将职责p细分为粒度更细的职责p1,p2,这时如果要使程序遵循单一职责原则,需要将类t也分解为两个类t1和t2,分别负责p1、p2两个职责。

但是在程序已经写好的情况下,这样做简直太费时间了。

所以,简单的修改类t,用它来负责两个职责是一个比较不错的选择,虽然这样做有悖于单一职责原则。

(这样做的风险在于职责扩散的不确定性,因为我们不会想到这个职责p,在未来可能会扩散为p1,p2,p3,p4……pn。

所以记住,在职责扩散到我们无法控制的程度之前,立刻对代码进行重构。

举例说明,用一个类描述动物呼吸这个场景:

牛呼吸空气

羊呼吸空气

猪呼吸空气

程序上线后,发现问题了,并不是所有的动物都呼吸空气的,比如鱼就是呼吸水的。

修改时如果遵循单一职责原则,需要将animal类细分为陆生动物类terrestrial,水生动物aquatic,代码如下:

classterrestrial{publicvoidbreathe(stringanimal){system.out.println(animal+呼吸空气classaquatic{publicvoidbreathe(stringanimal){system.out.println(animal+呼吸水publicclassclient{publicstaticvoidmain(string[]args){terrestrialterrestrial=newterrestrial();terrestrial.breathe(牛terrestrial.breathe(羊terrestrial.breathe(猪aquaticaquatic=newaquatic();aquatic.breathe(鱼运行结果:

牛呼吸空气

羊呼吸空气

猪呼吸空气

鱼呼吸水

我们会发现如果这样修改花销是很大的,除了将原来的类分解之外,还需要修改客户端。

而直接修改类animal来达成目的虽然违背了单一职责原则,但花销却小的多,代码如下:

classanimal{publicvoidbreathe(stringanimal){if(鱼.equals(animal)){system.out.println(animal+呼

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 法律文书 > 起诉状

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1