单一职责原则(SRP:Single responsibility principle)又称单一功能原则,面向对象五个基本原则(SOLID)之一
定义:不要存在多于一个导致类变更的原因。
通俗的说,即一个类只负责一项职责。
问题由来:有类T负责两个不同的职责:职责P1,职责P2。当由于职责P1需求发生改变而需要修改类T时,有可能会导致原本运行正常的职责P2功能发生故障。也就是说职责P1和P2被耦合在了一起。
解决方案:遵循单一职责原则。分别建立两个类T1、T2,使T1完成职责P1功能,T2完成职责P2功能。这样,当修改类T1时,不会使职责P2发生故障风险;
如果一个类承担的职责过多,就等于把这些职责耦合在一起了。一个职责的变化可能会削弱或者抑制这个类完成其他职责的能力。这种耦合会导致脆弱的设计,当发生变化时,设计会遭受到意想不到的破坏。而如果想要避免这种现象的发生,就要尽可能的遵守单一职责原则。此原则的核心就是解耦和增强内聚性。
没有任何的程序设计人员不清楚应该写出高内聚低耦合的程序,但是很多耦合常常发生在不经意之间,其原因就是:
职责扩散:因为某种原因,某一职责被分化为颗粒度更细的多个职责了。
每一个系统中的功能都表示了一个职责,这些职责可以映射到类中,且应该尽可能的保证这些模块之间没有功能上的重复。设计中每次应只关注一个职责。但这并不是其他的职责都抛之脑后,而是当决定用某个模块来实现某个职责之后,只需全神贯注于该职责相关(核心关注点)。从这个模块的角度出发,其他的职责都是不想关的。
同时模块拥有自己公开的接口,可以和其他模块通信,模块也包含大量内部信息供自身使用。模块中只有接口暴露出的成员才能供其他模块使用。内部数据不是被彻底隐藏,应被筛选后暴露给外部。这样,每个模块仍可以使用固定的接口来通信,而同时不会受到修改具体实现的影响。
举例说明:
public class StudentCadres{ public void manage(String name){ System.out.println(name+"管理班级"); } } public class Start{ public static void main(String[] args){ StudentCadres sc = new StudentCadres(); sc.manage("班长"); sc.manage("学习委员"); } }
运行结果:
班长管理班级
学习委员管理班级
但是并不是所有的学生干部都管理班级,如果将角色增加到学生会干部。
按上述代码完成功能会出现
public class StudentCadres{ public void manage(String name){ System.out.println(name+"管理班级"); } } public class Start{ public static void main(String[] args){ StudentCadres sc = new StudentCadres(); sc.manage("班长"); sc.manage("学习委员"); sc.manage("学生会干部"); } }
运行结果:
班长管理班级
学习委员管理班级
学生会干部管理班级
这显然并不能满足我们的要求,很显然,学生会主席并不会管理班级
自作聪明的程序员将代码修改成了如下方式:
public class StudentCadres{ public void manage(String name){ if(name.equlas("学生会主席") System.out.println(name+"管理学生会"); else System.out.println(name+"管理班级"); } } public class Start{ public static void main(String[] args){ StudentCadres sc = new StudentCadres(); sc.manage("班长"); sc.manage("学习委员"); sc.manage("学生会干部"); } }
运行结果:
班长管理班级
学习委员管理班级
学生会干部管理学生会
可以看到,这种修改非常简单,且能适用于目前的需求,但如果职责扩散,我们需要更细的职责粒度,将学生会干部扩散为学生会主席(管理学生会)和宣传干事(负责宣传教育相关事宜)又将如何?你不得不去修改StudentCadres里的manage方法,但修改manange方法时必然对班长和学习委员造成影响。
那么我们是否可以考虑如下的修改形式
public class StudentCadres{
public void manage(String name){
System.out.println(name+"管理班级");
}
public void manageStudentUnion(String name){
System.out.println(name+"管理学生会");
}
public void propaganda(String name){
System.out.println(name+"进行宣传");
}
}
public class Start{
public static void main(String[] args){
StudentCadres sc = new StudentCadres();
sc.manage("班长");
sc.manage("学习委员");
sc.manageStudentUnion("学生会主席");
sc.propaganda("宣传干事");
}
}
运行结果:
班长管理班级
学习委员管理班级
学生会主席管理学生会
宣传干事进行宣传
上述这种修改方式并没有改动原来的方法,而是在类中新加了两个方法用于表示管理学生会和进行宣传,这样虽然在Class层面上也违背了单一职责原则,但在方法级别上却是符合单一职责原则的,因为该类中每一个方法只完成一个职责,当某一职责发生变更时不会牵涉到其它职责,如果宣传干事罢工或者不负正业干了别的事,我们只需要修改propaganda方法即可,不会对学生会主席、班长、学习委员带来不好的影响;如果新增兴趣小组组长,我们只需要新增方法,也不会影响到学生会主席、班长、学习委员。
在重构的过程中我们也可以严格按照单一职责原则进行重构,那么我们在修改时需在Class层面上就遵循单一职责原则,将学生干部StudentCadres类细分为班干部类ClassCadre,学生会主席类StudentBodyPresident和宣传干事类PropagandaDirector代码如下:
public class ClassCadre{
public void manage(String name){
System.out.println(name+"管理班级");
}
}
public class StudentBodyPresident{
public void manage(String name){
System.out.println(name+"管理学生会");
}
}
public class PropagandaDirector{
public void manage(String name){
System.out.println(name+"进行宣传");
}
}
public class Start{
public static void main(String[] args){
ClassCadre cc = new ClassCadre();
cc.manage("班长");
cc.manage("学习委员");
StudentBodyPresident sbp = new StudentBodyPresident();
sbp.manageStudentUnion("学生会主席");
PropagandaDirector pd = new PropagandaDirector()
pd.propaganda("宣传干事");
}
}
运行结果:
班长管理班级
学习委员管理班级
学生会主席管理学生会
宣传干事进行宣传
这是一个严格按照单一职责原则重构的代码,一个类只负责一项职责,很显然,它的逻辑比上述重构(负责多项职责)简单了很多
综上所述,单一职责原则的优点如下:
1.降低职责间耦合度
2.降低模块设计的复杂度
3.提高代码的可读性
4.提高系统的可维护性
5.降低职责扩散或需求变更引发的风险
蜗牛学院,只为成就更好的你!这样创新的模式,值得你的选择!
还在等什么,赶快关注蜗牛学院官方微信,加入到蜗牛学院的大家庭中来吧!