单一原则
定义:
单一职责原则是最简单的面向对象设计原则,它用于控制类的粒度大小。单一职责原则定义如下: 单一职责原则(Single Responsibility Principle, SRP):一个类只负责一个功能领域中的相应职责,或者可以定义为:就一个类而言,应该只有一个引起它变化的原因。
问题由来:
当一个T类负责两个职责时,当职责P1需求发生变化了影响了T类,有可能导致原本运行正常的职责P2功能发生故障。
解决方案:
遵循单一职责,建两个类分别是T1、T2,它们分别负责职责P1、P2,当P1职责发生变化影响T1也发生变化,但是不影响P2的职责正常使用。
注意:
每个程序员即时没有读过设计模式、从来没有读过设计原则,在设计程序时也会尽量避免修改一个功能时影响其它功能的正常使用,这样便遵循了单一原则。虽然单一原则如此简单,并且被公认为常识,但是即时经验丰富的程序员也会有违背这一原则的存在。为什么会出现这种情况呢?因为有职责扩散。所谓职责扩散就是因为某些原因会将职责P分化成颗粒度更细的职责P1和P2。
例如
类T只负责一个职责P,这样设计是符合单一原则的,但是由于后期需求的变更或者是开发者能力的提升,需要将职责P分化成颗粒度更细的职责P1和职责P2,而这时要遵循单一原则的话,需要建T1和T2两个类,分别担负P1和P2的职责。但是这样做简直就是浪费时间。但是简单的修改类T,用它负责两个职责是一个不错的选择,虽然这样做有悖于单一原则。(这样做的风险在于职责扩散的不确定性,因为我们不会想到这个职责P未来可能会扩展成P1、P2、P3……Pn。当职责扩散到无法控制的时候,我们就需要对代码重构了,代价巨大)
举例说明
人们出行或者旅游出行方式的例子:
class TravelMode {
public void mode(String user){
System.out.println(user+"出行方式为火车");
}
}
public class Client {
public static void main(String[] args){
TravelMode travelMode = new TravelMode();
travelMode.mode("学生");
travelMode.mode("工人");
travelMode.mode("商人");
}
}
输出结果为:
程序上线后,发现问题存在,随着时代不断发展,出行方式也是层出不穷,比如商人需要出国谈生意就必须乘坐飞机。修改时如果遵循单一原则的话需要创建一个出行方式类细分为火车类Train和飞机类Airplane,他们都各自有各自的职责,代码如下:
public class Train {
public void plane(String user){
System.out.println(user+"出行方式为火车");
}
}
public class Airplane {
public void plane(String user){
System.out.println(user+"出行方式为飞机");
}
}
public class Client {
public static void main(String[] args){
TravelMode travelMode = new TravelMode();
travelMode.mode("学生");
travelMode.mode("工人");
Airplane airplane = new Airplane();
airplane.plane("商人");
}
}
输出结果为:
我们会发现如果这样修改花销是很大的,除了将原来的类分解之外,还需要修改客户端。而直接修改类TravelMode来达成目的虽然违背了单一职责原则,但花销却小的多,代码如下:
class TravelMode {
public void mode(String user){
if ("商人".equals(user)) {
System.out.println(user + "出行方式为飞机");
}else {
System.out.println(user + "出行方式为火车");
}
}
}
public class Client {
public static void main(String[] args){
TravelMode travelMode = new TravelMode();
travelMode.mode("学生");
travelMode.mode("工人");
travelMode.mode("商人");
}
}
输出结果为:
但是这种修改方式直接在代码级别上违背了单一原则,虽然改起来很简单,但是隐患确是最大的,因为后续可能还会出现轮船、开车、高铁等交通方式的出现,一旦发生职责扩散而需要修改类时,代价是巨大的。接下来还有一种方法虽然在类级别上不符合单一原则,但是在方法级别上却是符合单一原则的,因为他没有动原来的代码。
代码如下:
class TravelMode {
public void mode(String user){
System.out.println(user + "出行方式为火车");
}
public void mode2(String user){
System.out.println(user+"出行方式为飞机");
}
}
public class Client {
public static void main(String[] args){
TravelMode travelMode = new TravelMode();
travelMode.mode("学生");
travelMode.mode("工人");
travelMode.mode2("商人");
}
}
输出结果为:
遵循单一职责的优点:
- 可以降低类的复杂度,一个类只负责一项职责,其逻辑肯定要比负责多项职责简单的多;
- 提高类的可读性,提高系统的可维护性;
- 变更引起的风险降低,变更是必然的,如果单一职责原则遵守的好,当修改一个功能时,可以显著降低对其他功能的影响。
注:
单一职责原则不只是面向对象编程思想所特有的,只要是模块化的程序设计,都适用单一职责原则。