单一职责原则(Single Responsibility Principle)

news/2024/7/19 10:10:07 标签: 爬虫, java, c#

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

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

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

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

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

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

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

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

 1   public class Rectangle
 2   {
 3     public int Height { get; set; }
 4     public int Width { get; set; }
 5 
 6     public double Area()
 7     {
 8       return Width * Height;
 9     }
10 
11     public void Draw(Form form)
12     {
13       SolidBrush brush = new SolidBrush(Color.Red);
14       Graphics formGraphics = form.CreateGraphics();
15       formGraphics.FillRectangle(brush,
16         new System.Drawing.Rectangle(
17           new Point(0, 0), new Size(Width, Height)));
18     }
19   }

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

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

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

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

 1   public class ComputationalGeometryApplication
 2   {
 3     public double CalculateArea(Rectangle rectangle)
 4     {
 5       double area = rectangle.Area();
 6       return area;
 7     }
 8   }
 9 
10   public class GraphicalApplication
11   {
12     public Form form { get; set; }
13 
14     public void DrawOnScreen(Rectangle rectangle)
15     {
16       rectangle.Draw(form);
17     }
18   }

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

 1   public class GeometricRectangle
 2   {
 3     public int Height { get; set; }
 4     public int Width { get; set; }
 5 
 6     public double Area()
 7     {
 8       return Width * Height;
 9     }
10   }
11 
12   public class Rectangle
13   {
14     public void Draw(Form form, GeometricRectangle geometric)
15     {
16       SolidBrush brush = new SolidBrush(Color.Red);
17       Graphics formGraphics = form.CreateGraphics();
18       formGraphics.FillRectangle(brush,
19         new System.Drawing.Rectangle(
20           new Point(0, 0),
21           new Size(geometric.Width, geometric.Height)));
22     }
23   }

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

 1   public class ComputationalGeometryApplication
 2   {
 3     public double CalculateArea(GeometricRectangle geometric)
 4     {
 5       double area = geometric.Area();
 6       return area;
 7     }
 8   }
 9 
10   public class GraphicalApplication
11   {
12     public Form form { get; set; }
13 
14     public void DrawOnScreen(Rectangle rectangleDraw, GeometricRectangle rectangleShape)
15     {
16       rectangleDraw.Draw(form, rectangleShape);
17     }
18   }

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

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

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

1   public interface Camera
2   {
3     void Connect(string host);
4     void Disconnect();
5     void Send(byte[] data);
6     byte[] Receive();
7   }

然而,它确实耦合了 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

参考资料

  • 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》,未经作者本人同意禁止任何形式的转载,任何自动或人为的爬虫行为均为耍流氓。


http://www.niftyadmin.cn/n/1001114.html

相关文章

JAVA设计模式《二》

上一篇为大家介绍了一下设计模式中的责任链模式,本篇为大家介绍一下关于设计模式中的单例模式与模板方法模式。单例模式的作用在于,保证应用中某个实例有且只有一个,单例模式又被分为:饱汉模式与饿汉模式,两者的区别在…

【jquery】图片前后对比效果——beforeAfter

今天分享一款 jquery 插件——图片前后对比&#xff08;beforeAfter&#xff09;&#xff0c;效果如下&#xff1a; 使用方法&#xff1a; <!DOCTYPE HTML> <html lang"en"> <head><meta charset"utf-8"/><title>图片前后对…

TR5511规格书说明|替代TR5511EDP转LVDS方案|低BOM成本CS5211替代TR5511方案设计

TR5511是一种eDP到LVDS转换器&#xff0c;配置灵活&#xff0c;适用于低成本显示系统。TR5511符合eDP 1.2&#xff0c;支持每车道速度为1.62Gbps和2.7Gbps的1车道和2车道模式。凭借强大的SerDes技术&#xff0c;TR5511可以以低误码率恢复高速串行数据。TR5511 LVDS发射器支持单…

oracle 查询 函数练习2

/*以下代码是对emp表/dept表/salgrade表进行显示宽度设置 */col empno for 9999;col ename for a10;col job for a10;col mgr for 9999;col hiredate for a12;col sal for 9999;col comm for 9999;col deptno for 99;col dname for a14;col loc for a14;col grade for 9999;col…

路由器的修改特权密码、还原出厂设置、备份配置文件和升级操作系统实际操作...

修改特权密码1&#xff09;首先我们在思科的PT文件上创建一个路由器。2&#xff09;点开路由器进入命令行&#xff0c;首先设置一个密码&#xff0c;并保存。3&#xff09;保存成功后&#xff0c;假设你现在忘记了密码&#xff0c;那么现在关机重启&#xff0c;在启动过程中&am…

httpd与tomcat基于mod_jk整合

httpd与tomcat基于mod_jk整合 搞定在前面述, httpd与tomcat整合方式 当前已知的有 ajp_proxy,mod_jk.so jk connecteor连接器下载地址 http://archive.apache.org/dist/tomcat/tomcat-connectors/jk/ 具体搞定策略步骤 1,安装httpd,基于yum安装 yum install httpd httpd-devel…

leetcode------Permutation Sequence

标题&#xff1a;Permutation Sequence通过率&#xff1a;22.7%难度&#xff1a;中等The set [1,2,3,…,n] contains a total of n! unique permutations. By listing and labeling all of the permutations in order,We get the following sequence (ie, for n 3): "123…

微软云计算介绍与实践(实践之十四)

本章节主要是动态扩展负载的存储。因为操作步骤太多&#xff0c;要得分两章节&#xff08;十四、十五&#xff09;&#xff0c;不然写的累死&#xff0c;看的昏死&#xff0c;没好效果。又过了几天&#xff0c;晓红找到小张&#xff0c;告诉小张说他们正在开发的销售应用系统需…