单一职责原则(Single Responsibility Principle)
单一职责原则(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 | ||
| OCP | ||
| LSP | ||
|
ISP |
||
| DIP | ||
| LKP |
参考资料
- SRP:The Single Responsibility Principle by Robert C. Martin “Uncle Bob”
- The SOLID Principles, Explained with Motivational Posters
- Dangers of Violating SOLID Principles in C#
- An introduction to the SOLID principles of OO design
- 10 Golden Rules Of Good OOP
- 10 Object Oriented Design principles Java programmer should know
- SOLID Principles: Single Responsibility Principle
- Object Oriented Design Principles
本文《单一职责原则(Single Responsibility Principle)》由 Dennis Gao 翻译改编自 Robert Martin 的文章《SRP: The Single Responsibility Principle》,未经作者本人同意禁止任何形式的转载,任何自动或人为的爬虫行为均为耍流氓。
单一职责原则(Single Responsibility Principle)的更多相关文章
- 面象对象设计原则之一:单一职责原则(Single Responsibility Principle, SRP)
单一职责原则是最简单的面向对象设计原则,它用于控制类的粒度大小.单一职责原则定义如下:单一职责原则(Single Responsibility Principle, SRP):一个类只负责一个功能领域 ...
- 单一职责原则(Single Responsibility Principle,SRP)
定义:不要存在多于一个导致类变更的原因.通俗的说,即一个类只负责一项职责. 问题由来:类T负责两个不同的职责:职责P1,职责P2.当由于职责P1需求发生改变而需要修改类T时,有可能会导致原本运行正常的 ...
- 设计模式六大原则(一):单一职责原则(Single Responsibility Principle)
单一职责(SRP)定义: 不要存在多于一个导致类变更的原因,通俗的说,即一个类只负责一项职责. 问题由来: 类T负责两个不同的职责:职责P1,职责P2.当由于职责P1需求发生改变而需要修改类T时,有可 ...
- 01-01.单一职责原则(Single Responsibility)
1.基本介绍 对于类来说的,就是一个类,应该只负责一项职责(一个类只管一件事). 如类A负责两个不同职责:职责1,职责2. 当职责1需求变更而改变A时,可能造成职责2执行错误,所以需要将类A的粒度分解 ...
- 单一职责原则(Simple responsibility pinciple, SRP)
一个类只负责一个功能领域中的相应职责 未完待续
- 【设计模式】单一职责原则(SRP)
单一职责原则是面向对象原则五大原则中最简单,也是最重要的一个原则, 他的字面定义如下: 单一职责原则(Single Responsibility Principle, SRP): 一个类只负责一个功能 ...
- 【面向对象设计原则】之单一职责原则(SRP)
单一职责原则是面向对象原则五大原则中最简单,也是最重要的一个原则, 他的字面定义如下: 单一职责原则(Single Responsibility Principle, SRP): 一个类只负责一个功能 ...
- 2单一职责原则SRP
一.什么是单一职责原则 单一职责原则(Single Responsibility Principle ): 就一个类而言,应该仅有一个引起它变化的 原因. 二.多功能的山寨手机 山寨手机的功能: 拍照 ...
- 面向对象五大原则_1.单一职责原则&2.里氏替换原则
单一职责原则:Single Responsibility Principle (SRP) 一个类.仅仅有一个引起它变化的原因.应该仅仅有一个职责.每个职责都是变化的一个轴线.假设一个类有一个以上的职责 ...
随机推荐
- MVC 之 WebAPI 系列二
今天,我想在此记录下 WebApi 跨域调用 1. 什么叫跨域: 跨域问题简单理解就是JavaScript同源策略的限制,其根本原因是因为浏览器对于这种请求,所给予的权限是较低的,通常只允许调用本域中 ...
- 升级到VS2012,reportViewer无法使用
最近公司的开发环境升级到VS2012,为了电脑能够快点,我重装系统,只装VS2012没有装VS2010.这个时候问题来了,原本用VS2010开发的项目用VS2012编译能通过,运行时包下图错: 为了解 ...
- windows7安装python2.7及scrapy
http://www.cnblogs.com/txw1958/archive/2012/07/12/scrapy_installation_introduce.html 第0步:当然是安装python ...
- VS2012智能提示消失的解决方法
1.点击电脑左下角的“开始菜单”->"所有程序"->Microsoft Visual Studio 2012->Visual Studio Tools->V ...
- CSS 浮动副作用 ,清除浮动
参考:http://www.divcss5.com/jiqiao/j406.shtml 副作用:一般是一个盒子里使用了CSS float浮动属性,导致父级对象盒子不能被撑开,背景色不显示(如果父级不设 ...
- php 学习日志- 变量作用域
1.global 函数内访问全局变量 <?php $x=5; $y=10; function myTest() { global $x,$y; $y=$x+$y; } myTest(); ech ...
- 阿里云 centos 安装apache和php
mysql使用阿里云的rds httpd服务 1. 安装apr和apr-util 2. 安装 httpd apache.org,apr.apache.org 安装命令: ./configure --p ...
- [ MySql学习心得 ] --Two
五.MySql 中常用子句 1.where子句 我们都知道在查询数据时,未必会查整个表中的数据,当有条件查询时,就会用到where子句.其结构: select * from [表名] where ...
- 【转】mysql 拖库写库用法
mysqldump常用于MySQL数据库逻辑备份. 1.各种用法说明 A. 最简单的用法: mysqldump -uroot -pPassword [database name] > [dump ...
- 在Windows上编译最新的CURL,含有zlib,openssl
最近,从网上下载了一个curl库,使用时各种报错,都无法启动,于是干脆就直接自己编译了. 1. 准备工作 a. 下载zlib zlib可以使得HTTP请求支持gzip压缩,其地址如下: 官网:http ...