Java设计模式-单一职责原则
单一职责原则 【Single Responsibility Principle】
定义:不要存在多于一个导致类变更的原因。通俗的说,即一个类只负责一项职责。
问题由来:类T负责两个不同的职责:职责P1,职责P2。当由于职责P1需求发生改变而需要修改类T时,有可能会导致原本运行正常的职责P2功能发生故障。
解决方案:遵循单一职责原则。分别建立两个类T1、T2,使T1完成职责P1功能,T2完成职责P2功能。这样,当修改类T1时,不会使职责P2发生故障风险;同理,当修改T2时,也不会使职责P1发生故障风险。
说到单一职责原则,很多人都会不屑一顾。因为它太简单了。稍有经验的程序员即使从来没有读过设计模式、从来没有听说过单一职责原则,在设计软件时也会自觉的遵守这一重要原则,因为这是常识。在软件编程中,谁也不希望因为修改了一个功能导致其他的功能发生故障。而避免出现这一问题的方法便是遵循单一职责原则。虽然单一职责原则如此简单,并且被认为是常识,但是即便是经验丰富的程序员写出的程序,也会有违背这一原则的代码存在。为什么会出现这种现象呢?因为有职责扩散。所谓职责扩散,就是因为某种原因,职责P被分化为粒度更细的职责P1和P2。
举例说明:只要做过项目,肯定要接触到用户、机构、角色管理这个模块,我们今天要讲的是用户管理,管理用户的信息,增加机构(一个人属于多个机构),增加角色等,用户有这么的信息和行为要维护,我们就把这些写到一个接口中,都是用户管理类嘛,我们先来看类图:
太easy的类图了,我相信即使一个初级的程序员也可以看出这个接口设计的有问题,用户的属性(Properties)和用户的行为(Behavior)没有分开,这是一个严重的错误!非常正确,确实是这个接口设计的一团糟,应该把用户的信息抽取成一个业务对象(BussinessObject,简称B0),把行为抽取成另外一个接口中,我们把这个类图重新画一下:
这个类图看起来完全满足了类和接口的单一职责要求,非常符合标准,但是我相信你在设计的时候肯定不会采用这种方式,一个用户类要把把两个IUserB0和IUserBiz组合在一块才能使用,组合是一种强耦合关系,你和我都是有共同的生命期,这样的强耦合关系还不如使用接口实现的方式呢,而且还增加了类的复杂性,多了两个类呀,我们修改一下类图:
重新拆封成两个接口,IUserB0负责用户的属性,简单的说就是IUserB0的职责是收集和反馈用户的属性信息;IUserBiz负责用户的行为,完成用户的信息的维护和变更。各位可能要问了,这个和我实际的工作中用到的User类还是有差别的呀,别着急,我们先来想一想分拆成两个接口怎么使用,想清楚了,我们看是面向的接口编程,所有呀你产生了这个UserInfo对象之后,你当然可以把它当IUserB0接口使用,当然也可以当IUserBiz接口使用,这要看你在怎么地方使用了,你要获得用户想信息,你就当时IUserB0的实现类;你要是想维护用户的信息,就当是IUserBiz的实现类就成了,类似于如下代码:
IUserBiz userInfo=new UserInfo();
//我要赋值了,我就认为它是一个纯粹的
BO IUserBO userB0=(IUserBO)userInfo;
userBO.setPassword("abc");
//我要执行动作了,我就认为是一个业务逻辑类
IUserBiz userBiz=(IUserBiz)userInfo;
userBiz.deleteUser();
这样的设计,一个用户实现了两个接口,把两个职责融合一个类中,你会觉得这个UserInfo有两个原因引起变化了呀,是的是的,但是别忘记了我们是面向接口编程,我们对外公布的是接口而不是实现类;而且如果真要实现类的单一职责的话,这个就必须使用了上面组合的方式了,那这个会引起类间耦合过重的问题(我们等会再说明这个问题,继续往下看)。那使用了单一职责原则有什么好处呢?
单一职责使用于接口、类,同时也使用方法,什么意思呢?一个方法尽可能做一件事情,比如一个方法修改用户密码,别把这个方法放到“修改用户信息”方法中,这个方法的颗粒度很粗,比如这个一个方法:
在IUserManager中定义了一个方法叫changeuser,根据传递的type不同,把可变长度参数changeOptions修改到userBo这个对象上,并调用持久层的方法保存到数据库中。
这种方式要简单的多。但是却存在着隐患:每次新增、移除某个type都需要对原有代码进行修改啊;进而可能影响到其他type的操作,这种修改方式直接在代码级别上违背了单一职责原则,虽然修改起来最简单,但方法职责不清晰,不单一,隐患却是最大的。一般方法设计成这样的:
你要修改用户名称,就调用changeUserNane方法,你要修改家庭地址就调用changellomeAddress,你要修改单位单户就调用changeofficeTel方法,每个方法的职责就非常清晰,这也是一个良好的设计习惯。
对于接口,我们在设计的时候一定要做到单一,但是对于实现类就需要多方面考虑了,考虑项目环境,考虑工作量,考虑人员的技术水平,考虑硬件的资源情况等等,最终融合的结果是经常违背这一单一原则。生搬硬套单一职责原则会引起类的剧增,给维护带来非常多的麻烦;而且过分的细分类的职责也为人为的制造系统的负责性,本来一个类可以实现的行为非要拆成两个,然后使用聚合或组合的方式再耦合在一起,这个是人为制造了系统的复杂性,所以原则是死的,人是活的,这句话是非常好的。但是我相信随着技术的深入,单一职责原则必然会深入到项目的设计中去,而且这个原则是那么的简单,简单的我都不知道该怎么更加详细的去描述,单从字面上大家都应该知道是什么意思,单一职责嘛!
遵循单一职责原的优点有:
可以降低类的复杂度,一个类只负责一项职责,其逻辑肯定要比负责多项职责简单的多;
提高类的可读性,提高系统的可维护性;
变更引起的风险降低,变更是必然的,如果单一职责原则遵守的好,当修改一个功能时,可以显著降低对其他功能的影响。
需要说明的一点是单一职责原则不只是面向对象编程思想所特有的,只要是模块化的程序设计,都适用单一职责原则。
上一篇: MYSQL经典语句大全——开发篇