博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
单一职责原则
阅读量:6263 次
发布时间:2019-06-22

本文共 1603 字,大约阅读时间需要 5 分钟。

单一职责原则(Single Responsibility Principle, SRP)是Bob大叔提倡的S.O.L.I.D五大设计原则中的第一个。其中,职责(Responsibility)被表述为“变化的原因”(reason to change);SRP被表述为“一个类应该有且只有一个变化的原因”。但如果光从字面去理解,SRP很容易让人望文生义产生误解。本文希望能阐明SRP的本质,达到避免误解和指导设计的目的。

 

动机

对于设计原则的理解应该首先从它的动机入手,即遵循这个原则带来的好处是什么?对于SRP来讲,如果遵循“一个类应该有且只有一个变化的原因”是因,那么“任何一个变化只会影响一个类”就是果。可见SRP的动机主要是系统的可维护性,即降低适应变化的成本。

 

多功能与单变化

对于单功能的类来讲,比较容易遵循SRP,比如:把string转换为DateTime类型的工具类。理解SRP的难点在于在多功能类的情形,即不容易理解多功能与单变化的矛盾。让我们先来看一个Modem类的例子,Modem具有4个功能:拨号,挂断,发送数据,接收数据:

class Modem {

    public void Dial(string aPno) {…}

    public void Hangup() {…}

    public void Send(char aData) {…}

    public char Receive() {…}

}

我们先来考察一下Modem类的变化点,并将其分为两类:1.Modem类的内部细节变化,比如Dial、Hangup、Send、Receive等具体实现发生了变化;2.Modem类整体性的变化,比如Modem类需要增加Poweron和Poweroff方法。上面的Modem类具有多个变化点,不符合SRP。

 

抽象与抽象层次

但是,需要注意的是上面的Modem类其实是很符合现实的概念模型的,Modem本身就应该具有这几个功能点,也就是说Dial、Hangup、Send、Receive方法在Modem这个类中是高内聚关系。难道要把Modem类强行拆开吗,难道所有类都只应该有一个方法吗?是什么原因导致符合现实概念模型的Modem类设计与SRP不符合呢?问题在于抽象与抽象层次,Modem类是现实概念模型的抽象没有错,但是Modem类缺乏抽象层次,低层概念与高层概念混杂,这正是问题所在。下面是重构以后的Modem类:

interface IDialable{

    void Dial(string aPno);

}

class Dialer : IDialable{

    public void Dial(string aPno){…}

}

  

class Modem{

    public void Dial(string aPno) { m_dialer.Dial(aPno); }

    private IDialable m_dialer;

  

    … //hangup, send, receive类似

}

上面的设计将各个功能点抽象为单一的接口,将变化封装在稳定的接口背后,再通过组合的方式建立起整体的Modem类与局部的功能点的联系。这样就避免了低层的变化传导到高层。

 

总结

可见,理解SRP的关键在于理解类的抽象层次,高层次的类是高层概念的抽象,低层次的类是低层概念的抽象。低层的变化只影响低层类,高层的变化只影响高层类。对于遵守SRP的设计,一定具有很好的抽象层次,因此不妨以SRP为指导和检验,帮助我们设计出好的类来。最后,我用几个关键词梳理SRP的脉络:

类只有一个变化的原因 >> 一个变化只影响一个类 >> 变化只影响其相应层次的类

 

参考

 

PS:看过Bob大叔SRP相关介绍的朋友可能会发现,本文对SRP的论述并非完全照搬原著,有的观点甚至并不完全相同。欢迎指正本文的错误和不足,也欢迎就此话题进行讨论!

转载地址:http://exzpa.baihongyu.com/

你可能感兴趣的文章
[译] 怎样(以及为什么要)保持你的 Git 提交记录的整洁
查看>>
java中主线程等待所有子线程结束
查看>>
JavaScript中call,apply,bind方法的区别
查看>>
js 回顾知识总结一
查看>>
centeros bash: ifconfig: command not found
查看>>
leetcode Invert Binary Tree
查看>>
Python Requests快速入门
查看>>
[转] Invoke and BeginInvoke
查看>>
DataFrame的基本操作
查看>>
mysql02
查看>>
linux lftp命令
查看>>
多继承同名隐藏举例
查看>>
sql server 数据库忘记sa账户密码/ 无管理员账户解决办法
查看>>
试玩 PHP 5.4 的新特性
查看>>
Word该值小于列表中的前一条目
查看>>
第九周项目7-趣味编程
查看>>
JavaScript 函数式编程中的 curry 实现
查看>>
21.4 windows_21_Library_use_DLL 动态库补充4
查看>>
查看Eclipse运行工程时使用的Command Line
查看>>
使用WinExec打开文件夹
查看>>