01-单一职责原则(SPR)
1. 背景
类T负责两个不同的职责:职责P1,职责P2。当由于职责P1需求发生改变而需要修改类T时,有可能会导致原本运行正常的职责P2功能发生故障。
2. 定义
不要存在多于一个导致类变更的原因。通俗的说,即一个类只负责一项职责。
3. 宏观上
类层次上存在单一职责原则,同样方法层次上也存在单一职责原则。
4. 补充两个概念
高内聚:内聚是指类内部的属性和行为,高内聚就是指:一个类的属性和行为与这个类非常密切,称为高内聚。
低耦合:耦合是指类与类之间或者模块与模块之间的联系,低耦合就是指:耦合度低,易重用、使用灵活。
5. 案例
需求一:有牛和羊两种动物,它们都需要呼吸空气。
解决思路:构造动物类,封装呼气空气的方法。
1 public class Animal 2 { 3 public string myName { get; set; } 4 5 private string _name = null; 6 public Animal() 7 { 8 9 } 10 public Animal(string name) 11 { 12 this._name = name; 13 } 14 15 //封装呼吸空气的方法 16 public void BreathAir() 17 { 19 Console.WriteLine(this._name + "呼吸空气"); 21 } 22 }
需求变更:又多了鱼这个物种,它们需要呼吸空气。
解决方案一:直接在BreathAir上加判断,但是这样违背了方法意义上的单一职责原则。
1 public void BreathAir() 2 { 3 if (this._name.Equals("鱼")) 4 { 5 Console.WriteLine(this._name + "呼吸水"); //违背了方法级别的单一职责 6 } 7 else 8 { 9 Console.WriteLine(this._name + "呼吸空气"); 10 } 11 }
解决方案二:在Animal类中添加一个BreathWater的方法,但是这样违背了类意义上的单一职责原则。
1 //封装呼吸水的方法 2 public void BreathWater() 3 { 4 Console.WriteLine(this._name + "呼吸水"); 5 }
解决方案三:新建一个WaterAnimal类,在里面封装BreathWater的方法,无论是类级别还是方法级别,都满足了单一职责原则,但是改动成本太大,耗时太长。
1 public class WaterAnimal 2 { 3 private string _name = null; 4 public WaterAnimal(string name) 5 { 6 this._name = name; 7 } 8 //封装呼吸水的方法 9 public void BreathWater() 10 { 11 Console.WriteLine(this._name + "呼吸水"); 12 } 13 }
综合调用:
1 public static void show() 2 { 3 //下面模拟单一职责面对需求变更的情况。 4 //需求1:有牛和羊两种动物,它们需要呼吸空气 5 //解决思路:构造动物类→封装呼吸空气的方法 6 Animal animal1 = new Animal("牛"); 7 Animal animal2 = new Animal("羊"); 8 animal1.BreathAir(); 9 animal2.BreathAir(); 10 11 //需求2:又多了鱼这个物种,它们需要呼吸水 12 //解决方案一:在Breath里加判断,但是违背了方法级别的单一职责原则 13 Animal animal3 = new Animal("鱼"); 14 animal3.BreathAir(); 15 16 //解决方案二:在Animal类中添加一个BreathWater的方法,但是违背了类级别的单一职责原则 17 Animal animal4 = new Animal("鱼"); 18 animal4.BreathWater(); 19 20 //解决方案三:新建一个WaterAnimal类,在里面封装BreathWater的方法,无论是类级别还是方法级别,都满足了单一职责原则,但是改动成本太大,耗时太长 21 WaterAnimal animal5 = new WaterAnimal("鱼"); 22 animal5.BreathWater(); 23 24 }
解决方案四:利用委托来解决。
1 /// <summary> 2 /// 利用委托的插件式编程 3 /// </summary> 4 public static void show2() 5 { 6 Console.WriteLine("------------------下面是利用委托来解决--------------------"); 7 Animal animal1 = new Animal() { myName = "牛" }; 8 Animal animal2 = new Animal() { myName = "羊" }; 9 Animal animal3 = new Animal() { myName = "鱼" }; 10 Action<Animal> act1 = Utils.BreathWater; 11 Action<Animal> act2 = Utils.BreathAir; 12 act1.Invoke(animal1); 13 act1.Invoke(animal2); 14 act2.Invoke(animal3); 15 }
6. 单一职责原则的好处
(1). 降低类复杂性,一个类只负责一项职责要比负责多项职责简单的多。
(2). 提高类的可读性,便于维护。
(3). 变更引起的风险低,修改一个类的功能,显著降低对其他类的影响。
7. 我的总结
如果完全遵循单一职责原则,当业务复杂的时候,改动成本是最大的。所以我的原则是:根据实际业务场景,来决定是否违背方法或类层次上的单一职责原则。