单一职责原则
什么是单一职责原则
什么是单一职责原则?
单一职责原则的英文名称是Single Responsibility Principle,简称SRP。SRP的原话解释是:There should never be more than one reason for a class to change.
也就是说一个类,只有一个引起它变化的原因。应该只有一个职责。每一个职责都是变化的一个轴线,如果一个类有一个以上的职责,这些职责就耦合在了一起。
这会导致脆弱的设计。当一个职责发生变化时,可能会影响其它的职责。另外,多个职责耦合在一起,会影响复用性。例如:要实现逻辑和界面的分离。
什么是职责?
SRP中,把职责定义为"变化的原因"。如果你能想到N个动机去改变一个类,那么这个类就具有多于一个的职责。这里说的"变化的原因",只有实际发生时
才有意义。可能预测到会有多个原因引起这个类的变化,但这仅仅是预测,并没有真的发生,这个类仍可看做具有单一职责,不需要分离职责。比如我们生活
当中用的筷子,我们吃饭的时候既把筷子当作是刀来使用,用它来分割食物,也把筷子当做叉来使用,将食物夹到碗中或口中。在这其中,筷子既有分割食物的
职责,也有移动食物的职责,而西餐用的刀就只有分割食物的职责,叉只有移动食物的职责。我们可以根据具体的情况,可以把筷子看作是吃饭工具的职责,也
可以讲筷子看作具有分割食物与移动食物两个职责。所以说职责不是一成不变的,我们需要根据具体的情况来划分职责,决定职责颗粒度的大小。
具体实践
我们有时去国外出差,需要去银行兑换美元、欧元等等, 不同种类的钞票对应不同的汇率,汇率也会有所波动。类的设计如下,
class Exchange { private double _exchangeRate;
/// <summary> /// 汇率换算 /// </summary> /// <param name="type"></param> /// <returns></returns> public double GetExchangeRate(string type) { switch (type) { case "Dollar"://美元 return 0.15;
case "Euro"://欧元 return 0.08; } return 1; }
public double ExchangeMoney(double money,string type) { return money * GetExchangeRate(type); } } |
初看不会发现有什么问题,因为我们平时也是这样写的。我们认真看后会发现,当汇率发生变化时,我们需要修改Exchange类,当我们需要新增钞票台币时,
我们也需要修改Exchange类,而单一职责原则规定,只有一个变化的原因引起类的改变,因此违背了单一职责原则。此时就需要我们通过设计来实现单一职责原则了。
public class Money { public double ExchangeRate; }
class Euro:Money { public Euro() { ExchangeRate = 0.08; } }
class Exchange { public double ExchangeMoney(double money,Money type) { return money * type.ExchangeRate; } } |
上述设计,Money为基类,有属性ExchangeRate表示汇率,当新增一种钞票台币时,我们只需要新增一个类,继承自Money。当某种钞票的汇率发生变化时,
我们只需要在对应的类中修改对应类的汇率。每一个类只有在汇率发生变化时,才需要改变。满足单一职责原则,只有一个变化的原因能使类发生变化。
单一职责的好处
- 类的复杂性降低,实现什么职责都有清晰明确的定义;
- 可读性提高,复杂度降低,可读性提高;
- 可维护性提高,可读性提高,维护更容易;
- 适应变更的能力更强,自然风险就降低了;
总结
如果我们能对接口、类、方法都做到单一职责原则那当然是最好,但是每个类都实现单一职责原则,则需要使用组合模式,这会引起类间的耦合过重、类的
数量增加,这也增加了设计的复杂性。对于单一职责原则,建议是接口一定要做到单一职责原则,类的设计尽量做到只有一个原因引起变化。