这是SOLID原则这一系列的第二篇文章,主要来描述单一职责(SRP)原则。SRP指出“每一个类或者类似的代码块应该只有一个职责,所以只有一个原因会使其改变”。
单一职责原则(SRP)声明"一个类的改变的原因绝对不能超过一个"。这意味着你的类或者结构体都应该只有一个工作要做。类中的一切都应该与单一的目的相关。这并不意味着你的类中只包含一个函数或一个属性。只要它符合单一职责,他可以有很多成员。当因为某一原因而发生改变的时候,多个类的成员可能需要修改。也可能造成多个类将需要更新。应用到SRP的应用可以大大改善你的代码。一个关键的变化是你项目中的类将变得更小更整洁。创建紧密集中在单一职责上的类会产生更容易理解和维护的代码。
小而内聚的类的另外一个好处是包含错误的几率降低了。这减少了对更改的需求,因而代码变得不那么脆弱了。Class只履行一项职责,多个Class将一起工作来实现更大的任务。
示例代码
为了演示SRP的应用,我们可以考虑一个违反它的C#类示例,并解释如何遵循原则重构类:
public class OxygenMeter
{
public double OxygenSaturation { get; set; }
public void ReadOxygenLevel()
{
using (MeterStream ms = new MeterStream("O2"))
{
int raw = ms.ReadByte();
OxygenSaturation = (double)raw / 255 * 100;
}
}
public bool OxygenLow()
{
return OxygenSaturation <= 75;
}
public void ShowLowOxygenAlert()
{
Console.WriteLine("Oxygen low ({0:F1}%)", OxygenSaturation);
}
}
上面的代码是与硬件设备进行通信以监测某些水中的氧含量的类。该类包括一个名为“ReadOxygenLevel”的方法,该方法从氧监测硬件生成的流中获取值。它转换取到的value为一个百分比然后保存在OxygenSaturation属性中。第二种方法“OxygenLow”检查氧饱和度,以确保其超过最低水平的75%。ShowLowOxygenAlert”显示包含当前饱和度值的alert。
OxygenMeter类中至少有三个更改原因。如果替代了氧气监测硬件,则需要更新ReadOxygenLevel方法。 如果确定低氧的过程发生变化,也许包括温度变量,则该类将需要更新。 最后,如果告警要求比将文本输出到控制台更复杂,则ShowLowOxygenAlert方法将需要重写。
代码重构
重构代码我们将功能分离到三个类中。第一个是“OxygenMeter”类。 这将包含OxygenSaturation属性和ReadOxygenLevel方法。你可以决定将这些成员分成不同的类。 在这种情况下,我们会把它们保持在一起,因为它们是密切相关的。删除其他方法,以便更改的唯一原因是更换监控硬件。
第二个类叫“OxygenSaturationChecker”。该类包括将氧气水平与最小可接受值进行比较的单一方法。该方法与原始版本相同,只是添加了一个注入含有要测试的饱和度级别的OxygenMeter对象的参数。类改变的唯一原因是测试过程发生变化。最后一个类为“OxygenAlerter”。 这显示包括当前氧饱和度水平的alert。 再次注入OxygenMeter的依赖关系。 类改变的一个原因是如果报警系统被更新。注意:下面的重构示例代码打破了其他SOLID原则,以便SRP的应用程序可见。
public class OxygenMeter
{
public double OxygenSaturation { get; set; }
public void ReadOxygenLevel()
{
using (MeterStream ms = new MeterStream("O2"))
{
int raw = ms.ReadByte();
OxygenSaturation = (double)raw / 255 * 100;
}
}
}
public class OxygenSaturationChecker
{
public bool OxygenLow(OxygenMeter meter)
{
return meter.OxygenSaturation <= 75;
}
}
public class OxygenAlerter
{
public void ShowLowOxygenAlert(OxygenMeter meter)
{
Console.WriteLine("Oxygen low ({0:F1}%)", meter.OxygenSaturation);
}
}