你的位置:首页 > ASP.net教程

[ASP.net教程]敏捷软件开发 – SRP 单一职责原则


SRP:单一职责原则  一个类应该只有一个发生变化的原因。

 

       为何把两个职责分离到单独的类中很重要呢?因为每一个职责都有变化的一个轴线。当需求变化时,该变化会反映为类的职责的变化。如果一个类承担了多于一个的职责,那么引起它变化的原因就会有多个。

       如果一个类承担的职责过多,就等于把这些职责耦合在了一起。一个职责发生变化可能会削弱或抑制这个类完成其他职责的能力。这种耦合会导致脆弱的设计,当变化发生时,设计会遭受到意想不到的破坏。

       有两个不同的应用程序使用Rectangle类。一个应用程序是有关计算几何学方面的,利用Rectangle类计算几何形状,但不会再屏幕上绘制矩形。另外一个应用程序实质上是有关图形绘制方面的,它可能也会进行一些计算几何方面的工作,但是它肯定会在屏幕上绘制矩形。

       这个设计违反了单一职责原则(SRP)。Rectangle类具有两个职责。第一个职责提供了一个矩形几何形状的数学模型;第二个职责是把矩形在一个图形用户界面上绘制出来。

       对于SRP的违反导致了一些严重的问题。首先,我们必须在计算几何应用程序中包含进GUI代码。在.Net中,就必须要把GUI组建和计算几何应用一起构建、部署。

       其次,如果GraphicalApplication的改变由于一些原因导致了Rectangle的改变,那么这个改变会迫使我们重新构建、测试以及部署ComputationalGeometryApplication。如果忘记这样做,ComputationalGeometryApplication可能会以不可预测的方式失败。

       一个较好的设计是把这两个职责分离到两个完全不同的类中。这个设计把Rectangle类中进行计算的部分移动到了GeometricRectangle类中。现在矩形绘制方式的改变不会对ComputationalGeometryApplication类造成影响。

 

 

定义职责

       在SRP中,我们把职责定义为变化的原因。如果你能够想到多于一个的动机去改变一个类,那么这个类就具有多于一个的职责。有时,我们很难注意到这一点。我们习惯于以组的形式去考虑职责。

public interface Modem{    public void Dial(string pno);    public void Hangup();    public void Send(char c);    public char Recv();}

  这个接口显示出两个职责。第一个职责是连接管理;第二个职责是数据通信。Dial和Hangup函数进行调制解调器的连接处理,而Send和Recv函数进行数据通信。

  这两个职责应该分开吗?这依赖于应用程序变化的方式。如果应用程序的变化会影响到连接函数的签名(signature),那么这个设计就具有僵化性的臭味,因为调用Send和Recv的类必须要重新编译、部署的次数常常会超过我们希望的次数。在这种情况下,这两个职责应该被分离。这样做避免了客户应用程序和这两个职责耦合在一起。

 

       另一方面,如果应用程序的变化方式总是导致这两个职责同时变化,那么就不必分离它们,实际上,分类它们就会具有不必要的复杂性的臭味。

       在此还有一个推论。仅当变化发生时,变化的轴线才具有实际意义。如果没有征兆,那么应用SRP或者任何其他原则都是不明智的。

 

分离耦合的职责

       请注意,上面把两个职责都耦合进了Modem类中。这不是所希望的,但是或许是必要的。常常会有一些和硬件或者操作系统的细节有关的原因,迫使我们把不愿耦合在一起的东西耦合在了一起。然而,对于应用程序其他部分来说,通过分类它们的接口我们已经解耦了概念。

       我们可以把Modem类看作是一个杂凑物,或者有缺陷的类。然后,请注意所有的以来关系都是从它发出的。谁都不需要依赖于它。除了main外,谁也不需要知道它的存在。因此,我们已经把丑陋的部分隐藏起来了。其丑陋性不会泄漏出来,污染应用程序的其他部分。

 

结论

       SRP是所有原则中最简单的原则之一,也是最难正确运用的原则之一。我们会自然地把职责结合在一起。软件设计真正要做的许多工作,就是发现职责并把那些职责相互分离。

 

摘录自:[美]RobertC.Martin、MicahMartin著,邓辉、孙鸣译 敏捷软件开发原则、模式与实践(C#版修订版) [M]、人民邮电出版社,2013、89-92、