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

一个类应该有且只有一个变化的原因。

There should never be more than one reason for a class to change.

为什么将不同的职责分离到单独的类中是如此的重要呢?

因为每一个职责都是一个变化的中心。当需求变化时,这个变化将通过更改职责相关的类来体现。

如果一个类拥有多于一个的职责,则这些职责就耦合到在了一起,那么就会有多于一个原因来导致这个类的变化。对于某一职责的更改可能会损害类满足其他耦合职责的能力。这样职责的耦合会导致设计的脆弱,以至于当职责发生更改时产生无法预期的破坏。

例如,考虑下图中的设计。类图中显示 Rectangle 类包含两个方法,一个方法(Draw)负责在显示屏幕上绘制矩形,另一个方法(Area)负责计算矩形图形面积。

有两个不同的应用程序均使用了 Rectangle 类。一个应用为计算几何程序,它使用了 Rectangle 中的数学几何模型,但不会在显示屏幕上绘制矩形。另一个应用是一个图形界面程序(GUI),它可能会做一些计算几何方面的工作,但主要功能是在屏幕上绘制矩形。

   public class Rectangle
{
public int Height { get; set; }
public int Width { get; set; } public double Area()
{
return Width * Height;
} public void Draw(Form form)
{
SolidBrush brush = new SolidBrush(Color.Red);
Graphics formGraphics = form.CreateGraphics();
formGraphics.FillRectangle(brush,
new System.Drawing.Rectangle(
new Point(, ), new Size(Width, Height)));
}
}

这个设计侵犯了 SRP 原则。Rectangle 类包含了两个职责。第一个职责是提供矩形几何计算的数学模型,第二个职责是在 GUI 上渲染矩形。

对 SRP 原则的侵犯会导致诸多难以解决的问题:

首先,我们必须在计算几何应用中包含对 GUI 库的引用。这导致应用程序无谓的消耗了链接时间、编译时间、内存空间和存储空间等。

再者,如果因为某些原因对 GraphicalApplication 的一个更改导致 Rectangle 类也相应做了更改,这将强制我们对 ComputationalGeometryApplication 进行重新编译、重新测试和重新部署等。如果我们忘了做这些事情,那么应用程序可能以无法预期的方式而崩溃。

   public class ComputationalGeometryApplication
{
public double CalculateArea(Rectangle rectangle)
{
double area = rectangle.Area();
return area;
}
} public class GraphicalApplication
{
public Form form { get; set; } public void DrawOnScreen(Rectangle rectangle)
{
rectangle.Draw(form);
}
}

一个较好的设计是将这两个职责完全地隔离到不同的类当中,如下图所示。这个设计将 Rectangle 中关于几何计算的职责移到了 GeometricRectangle 类中,而 Rectangle 类中仅保留矩形渲染职责。

   public class GeometricRectangle
{
public int Height { get; set; }
public int Width { get; set; } public double Area()
{
return Width * Height;
}
} public class Rectangle
{
public void Draw(Form form, GeometricRectangle geometric)
{
SolidBrush brush = new SolidBrush(Color.Red);
Graphics formGraphics = form.CreateGraphics();
formGraphics.FillRectangle(brush,
new System.Drawing.Rectangle(
new Point(, ),
new Size(geometric.Width, geometric.Height)));
}
}

然后,如果我们再对 Rectangle 中渲染职责进行更改时将不会再影响到 ComputationalGeometryApplication 了。

   public class ComputationalGeometryApplication
{
public double CalculateArea(GeometricRectangle geometric)
{
double area = geometric.Area();
return area;
}
} public class GraphicalApplication
{
public Form form { get; set; } public void DrawOnScreen(Rectangle rectangleDraw, GeometricRectangle rectangleShape)
{
rectangleDraw.Draw(form, rectangleShape);
}
}

那么,职责(Responsibility)到底是什么?

单一职责原则(SRP:Single Responsibility Principle)的概念中,我们将职责(Responsibility)定义为 "一个变化的原因(a reason for change)"。如果你能想出多于一种动机来更改一个类,则这个类就包含多于一个职责。

职责的耦合有时很难被发现,因为我们习惯于将多个职责一起来考虑。例如,我们考虑下面定义的 Camera 接口,可能会认为这个接口看起来是非常合理的。接口中声明的 4 个方法从属于一个 Camera 接口定义。

   public interface Camera
{
void Connect(string host);
void Disconnect();
void Send(byte[] data);
byte[] Receive();
}

然而,它确实耦合了 2 个职责。第一个职责是连接管理,第二个职责是数据通信。Connect 和 Disconnect 方法负责管理 Camera 与管理端 Host 的连接,而 Send 和 Receive 方法则负责收发通信数据。

这两个职责应该被分离吗?答案基本上是肯定的。这两组方法基本上没有任何交集,它们都可以依据不同的原因而变化。进一步说,它们将在应用程序中完全不同的位置被调用,而那些不同的位置将同样会因不同的原因而变化。

因此,下图中的设计可能会好一些。它将这两个职责分别隔离到不同的接口定义中,这至少使应用程序从两个职责中解耦。

然而,我们注意到这两个职责又重新被耦合进了一个 CameraImplementation 类中。这可能不是我们想要的,但却有可能是必须的。通常有很多原因会强制我们将一些职责耦合在一起。尽管如此,我们使得应用程序的其他部分得益于这个接口的隔离。

CameraImplementation 类在我们看来是一个组合出来的但确实包含一些缺点的类。但需要注意到的是,所有其他需要使用 CameraImplementation 类的地方已经可以被接口进行隔离,我们仅需依赖所定义的单一职责的接口。而 CameraImplementation 仅在被实例化的位置才会出现。我们将丑陋的代码限制在一定的范围内,而不会泄露或污染应用程序的其他部分。

总结

单一职责原则(SRP:Single Responsibility Principle)可表述为 "一个类应该有且只有一个变化的原因(There should never be more than one reason for a class to change.)"。单一职责原则是一个非常简单的原则,但通常也是最难做的正确的一个原则。职责的联合是在实践中经常碰到的事情,从这些各不相同的职责中发现并隔离职责就是软件设计的真谛所在。我们所讨论的其他设计原则最终也会回归到这个原则上来。

面向对象设计的原则

 SRP

单一职责原则

Single Responsibility Principle

 OCP

开放封闭原则

Open Closed Principle

 LSP

里氏替换原则

Liskov Substitution Principle

ISP

接口分离原则

Interface Segregation Principle

 DIP

依赖倒置原则

Dependency Inversion Principle

 LKP

最少知识原则

Least Knowledge Principle

参考资料

本文《单一职责原则(Single Responsibility Principle)》由 Dennis Gao 翻译改编自 Robert Martin 的文章《SRP: The Single Responsibility Principle》,未经作者本人同意禁止任何形式的转载,任何自动或人为的爬虫行为均为耍流氓。

单一职责原则(Single Responsibility Principle)的更多相关文章

  1. 面象对象设计原则之一:单一职责原则(Single Responsibility Principle, SRP)

    单一职责原则是最简单的面向对象设计原则,它用于控制类的粒度大小.单一职责原则定义如下:单一职责原则(Single Responsibility Principle, SRP):一个类只负责一个功能领域 ...

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

    定义:不要存在多于一个导致类变更的原因.通俗的说,即一个类只负责一项职责. 问题由来:类T负责两个不同的职责:职责P1,职责P2.当由于职责P1需求发生改变而需要修改类T时,有可能会导致原本运行正常的 ...

  3. 设计模式六大原则(一):单一职责原则(Single Responsibility Principle)

    单一职责(SRP)定义: 不要存在多于一个导致类变更的原因,通俗的说,即一个类只负责一项职责. 问题由来: 类T负责两个不同的职责:职责P1,职责P2.当由于职责P1需求发生改变而需要修改类T时,有可 ...

  4. 01-01.单一职责原则(Single Responsibility)

    1.基本介绍 对于类来说的,就是一个类,应该只负责一项职责(一个类只管一件事). 如类A负责两个不同职责:职责1,职责2. 当职责1需求变更而改变A时,可能造成职责2执行错误,所以需要将类A的粒度分解 ...

  5. 单一职责原则(Simple responsibility pinciple, SRP)

    一个类只负责一个功能领域中的相应职责 未完待续

  6. 【设计模式】单一职责原则(SRP)

    单一职责原则是面向对象原则五大原则中最简单,也是最重要的一个原则, 他的字面定义如下: 单一职责原则(Single Responsibility Principle, SRP): 一个类只负责一个功能 ...

  7. 【面向对象设计原则】之单一职责原则(SRP)

    单一职责原则是面向对象原则五大原则中最简单,也是最重要的一个原则, 他的字面定义如下: 单一职责原则(Single Responsibility Principle, SRP): 一个类只负责一个功能 ...

  8. 2单一职责原则SRP

    一.什么是单一职责原则 单一职责原则(Single Responsibility Principle ): 就一个类而言,应该仅有一个引起它变化的 原因. 二.多功能的山寨手机 山寨手机的功能: 拍照 ...

  9. 面向对象五大原则_1.单一职责原则&2.里氏替换原则

    单一职责原则:Single Responsibility Principle (SRP) 一个类.仅仅有一个引起它变化的原因.应该仅仅有一个职责.每个职责都是变化的一个轴线.假设一个类有一个以上的职责 ...

随机推荐

  1. ADV-时间分配

    #include<stdio.h> int map[20][4]; typedef struct node{ int star; int end; }node; node dui[100] ...

  2. [mk] 喝一杯咖啡, 写一写 Makefile

    Makefile 是 Linux 下组织程序的一个工具,它的命令是 make. (首字母M/m都可以) [Makefile] Makefile 编写的主旋律: target: [dependency] ...

  3. 云计算P2V的迁移过程

    总结一下客户在传统物理机向虚拟化环境迁移中的典型场景和常用工具及步骤.

  4. 使用Scala实现文件单词计数

    package com.dtapp.base import scala.io.Source object WCntApp { def main(args: Array[String]) { val f ...

  5. JQuery初体验

    虽然做b/s也有一年半了,但是还没怎么认真的去看JQuery,趁自己生病的这几天,恶补一下JQuery方面的知识,保持学习的态度,内容很简单,聊以自慰一下>_<.废话不多说,直接上代码了. ...

  6. js 正则验证输入框只允许输入正实数和正整数和负整数

    <input onkeyup="this.value=this.value.replace(/[^0-9.]/g,'')">  (正实数) <input onke ...

  7. MVC 3 IIS7.5 网站发布及IIS配置文件问题处理

    1.环境配置 1) IIS7.5 2)VS2010 完整版 2.配置internet信息服务功能,直接上图,简洁明了. 3.打开VS2010 ,网站发布, 4.IIS网站设置 添加网站, 5-在浏览器 ...

  8. MFC修改初始窗口大小和窗口名字禁止窗口最大,最小化

    2,在里面就可以修改初始窗口大小和窗口名字 BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs){if( !CFrameWnd::PreCrea ...

  9. SSE and Websocket

    http://www.w3school.com.cn/html5/html_5_serversentevents.asp http://javascript.ruanyifeng.com/htmlap ...

  10. Sharepoint添加顶部导航菜单

    网站设置->导航->全局导航添加链接->设置标题和url->保存