设计模式六大原则——单一职责原则

单一职责原则(Single Responsibility Principle, SRP)

单一职责原则定义:就一个类而言,应该仅有一个引起它变化的原因。或者说,一个类只负责一项职责。

单一职责原则是实现高内聚、低耦合的指导方针,它是最简单又是最难用的原则。
问题提出:假如一个类负责两项职责,R1和R2。当职责R1的需求发生变化的时候,需要对该类进行修改,这个修改可能会影响到职责R2的功能。

问题解决:这个问题的解决方案是很容易想到的,即遵循单一职责原则。分别建立两个类C1和C2,分别去实现职责R1和R2。在职责R1的需求发生变化的时候,可以直接修改类C1,而不用担心R2的功能收到影响;职责R2的需求变更亦然。

软件工程真正要做的许多内容,就是发现职责并把那些职责相互分离。

那么怎么去判断一个类拥有多个职责呢?

如果你可以找到不止一个动机去修改一个类,那么说明这个类不止一个职责,这个时候就需要对这个类进行职责分离。
可以利用动物觅食这个简单的例子来理解单一职责原则:

1
2
3
4
5
6
7
8
9
10
11
12
13
class Animal{
pubilc void eat(String animal){
System.out.println(animal + " eat grass.");
}
}
public class Client{
public static void main(String args[]){
Animal animal = new Animal();
animal.eat("Cow");
animal.eat("Sheep");
animal.eat("Lion");
}
}

运行结果:

1
2
3
Cow eat grass.
Sheep eat grass.
Lion eat grass.

显然,这个结果是有错误的,狮子怎么能吃草呢?因为动物除了食草动物还有食肉动物(这里举例就不考虑杂食动物了)。很容看出来,此时Animal类不仅要负责食草动物的吃,还要负责食肉动物的吃,显然违背了单一职责原则。这个时候需要对Animal类进行修改,添加食肉动物的eat方法。但是如果要遵循单一职责原则,应该将Animal类细分为Herbivore(食草动物)和Carnivore(食肉动物)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Herbivore{
public void eat(String herbivore){
System.out.println(herbivore + " eat grass.");
}
}

class Carnivore{
public void eat(String carnivore){
System.out.println(carnivore + " eat meat.");
}
}

public class Client{
public static void main(String args[]){
Herbivore herbivore = new Herbivore();
herbivore.eat("Cow");
herbivore.eat("Sheep");
Carnivore carnivore = new Carnivore();
carnivore.eat("Lion");
}
}

这个时候可能会发现这样的修改开销太大,直接在原Animal中修改eat方法,开销小得多,如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Animal{
pubilc void eat(String animal){
if("Lion".equals(animal))
System.out.println(animal + " eat meat.");
else
System.out.println(animal + " eat grass.");
}
}
public class Client{
public static void main(String args[]){
Animal animal = new Animal();
animal.eat("Cow");
animal.eat("Sheep");
animal.eat("Lion");
}
}

虽然这种修改方法的开销小得多,但是隐患却很大。因为之后可能还会有老虎吃肉,鳄鱼吃肉等等,还回去修改这个类,显然这是不提倡的。
当然还有另一种修改方式,可以直接在Animal类中增加食肉动物吃的方法,即:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Animal{
pubilc void eat(String animal){
System.out.println(animal + " eat grass.");
}
public void carnivore_Eat(String animal){
System.out.println(animal + " eat meat.");
}
}
public class Client{
public static void main(String args[]){
Animal animal = new Animal();
animal.eat("Cow");
animal.eat("Sheep");
animal.carnivore_Eat("Lion");
}
}

显然这样的方式也违背了原始的单一职责原则,但是它却遵循了方法上的单一职责原则,因为它不对原始的方法造成影响。在实际开发中选择原始的单一职责原则,还是方法上的单一职责原则,需要根据实际情况而定。
通过上面的例子,可以看出单一职责原则的优点:
1、降低类的复杂度,简化逻辑,一个类只负责一项职责;
2、提高类的可读性,易于维护;
3、显著降低变更风险,修改一个功能时,对其他功能的影响很小。