问题由来:
之所以会出现单一职责原则就是因为在软件设计时会出现以下类似场景:
T负责两个不同的职责:职责P1,职责P2。当由于职责P1需求发生改变
而需要修改类T时,有可能会导致原本运行正常的职责P2功能发生故障。
也就是说职责P1和P2被耦合在了一起。
产生原因:
没有任何的程序设计人员不清楚应该写出高内聚低耦合的程序,但是
很多耦合常常发生在不经意之间,其原因就是:
职责扩散:因为某种原因,某一职责被分化为颗粒度更细的多个
职责了。
解决办法:
遵守单一职责原则,将不同的职责封装到不同的类或模块中。
定义
对于一个类,应该只有一个引起它变化的原因。此原则的核心就是解耦和增强内聚性。
理解
比如一个负责接收用户发来的信息的类,可能会被设计为还可以访问数据库将用户发来的信息进行记录等等。这样就意味着将来数据库持久层发生变化也会引起这个类的变化;而另一个方面,如果用户发送的信息的结构改变了,也会引起这个类的变化。也就是说,引起这个类发生变化的原因有两个,这就违背了单一职责原则了。
作用
(1)代码易于维护,遵循面向对象的基本原则:代码复用
举个栗子:
//以前的超人类
public class SuperMan{
public void 维护世界和平(){
//超人在维护世界和平
......
}
public void 除草(){
//超人在帮邻家大婶除草
......
}
public void 写代码(){
//超人化身为程序员
......
}
public void 写作业(){
//超人在帮小朋友写作业
......
}
......
......
}
问题:
(1)以前的超人什么都做,又当保姆,又做程序员,又做学生...但是一旦
超人的某一项功能出现问题时,程序员就要重新“修改”超人,这时就没
有人站出来维护世界和平了,必须等待超人被“修改”完后才有人维护和平。
(2)当一个类的职责的过于庞大时,维护起来就很麻烦,甚至难于
维护和阅读。
(3)
如果按照单一职责原则设计的话,超人类会变成以下一样:
//现在的超人类
public class SuperMan{
public void 维护世界和平(){
//超人在维护世界和平
......
}
}
//现在超人就只有一个职责就是维护世界和平,
//再也不用去当保姆,做程序员,做学生了。
(2)避免修改一个功能从而影响到其他功能出现问题
举栗子:设计一个类描述动物的呼吸功能
class Animal{
//呼吸功能
public void breathe(String animal){
system.out.println(animal + "需要呼吸");
}
}
//客户端实现
public class Client{
public static void main(String[] args){
Animal animal = new Animal();
animal.breathe("狗");
animal.breathe("猫");
animal.breathe("羊");
}
}
/*********************************************/
运行结果:
狗需要呼吸
猫需要呼吸
羊需要呼吸
/*********************************************/
-------------------------------------------------------
程序运行后,就出现了一个问题,世界上不是所有动物都呼吸空气的,
比如鱼呼吸的时候就是吞吐水。下面有3个方法修改程序:
-------------------------------------------------------
1)将Animal类分成陆生生物类和水生生物类
//陆生生物类
class Terrestrial{
public void breathe(String animal){
System.out.println(animal + "在空气中呼吸");
}
}
//水生生物类
class Aquatic{
public void breathe(String animal){
System.out.println(animal + "在水中呼吸");
}
}
//客户端实现
public class Client{
public static void main(String[] args){
Terrestrial terrestrial = new Terrestrial();
terrestrial.breathe("狗");
terrestrial.breathe("猫");
terrestrial.breathe("羊");
Aquatic aquatic = new Aquatic();
quatic.breathe("鱼");
}
}
这种方法虽然遵从单一职责原则,但是这样对代码的改动很大,除了将之前
的动物类分成两个之外,还要修改客户端的代码。
---------------------------------------------------------
(2)直接修改Animal类
//动物类
class Animal{
public void breathe(String animal){
if("鱼".equals(animal)){
System.out.println(animal + "在水中呼吸");
}else{
System.out.println(animal + "在空气中呼吸");
}
}
}
//客户端
public class Client{
public static void main(String[] args){
Animal animal = new Animal();
animal.breathe("狗");
animal.breathe("猫");
animal.breathe("羊");
animal.breathe("鱼");
}
}
这种方法虽然违背单一职责原则,资源的开销却小得多。但也存在着隐患,
假如有一天需要将鱼分为在淡水中呼吸的和在海水中呼吸的,那就需要修
改Animal类中breathe方法,而这种修改可能会对调用“狗”“猫”等相关
功能带来风险,也许某一次程序员会发现程序输出“狗在水中呼吸”了。
---------------------------------------------------------
(3)在animal类中新增一个呼吸功能的方法
//动物类
class Animal{
public void breathe(String animal){
System.out.println(animal + "在空气中呼吸");
}
public void breathe2(String animal){
System.out.println(animal + "在水中呼吸");
}
}
//客户端
public class Client{
public static void main(String[] args){
Animal animla = new Animal();
animal.breathe("狗");
animal.breathe("猫");
animal.breathe("羊");
animal.breathe2("鱼");
}
}
这种方法没有改动原有的方法,而是再类中新增了一个方法,这样
虽然也是违背了单一职责原则,但类在方法级别上却是符合单一职
责原则的,因为这样修改并没有改动原来的方法代码。
-------------------------------------------------------
小结:
(1)当一个类承担过多的职责时,就等同于将这些职责耦合在一起,
一个职责的变化就会削弱这个类完成其他职责的能力。
(2)遵循单一职责原则的好处有:
a)降低类的复杂度,一个类只负责一项职责,
b)提高类的可读性,
c)提高系统的可维护性。
(3)其实面向对象的软件设计几乎就是一个发现职责并且将其互相
分离的过程。至于如何做到单一职责原则,其实也很简单,如果能够想
到多于一个的动机去改变一个类,那么这个类就具有多于一个的职责,
就需要我们将这些职责进行分离。
参考:《设计模式其实很简单》和《百度百科》