You are here

我对“单一责职原理”的理解和它的哲学意义

eureka 的头像

  根据维基百科英文版关于单一职责原则(Single responsibility principle)的介绍:single responsibility principle states that every object should have a single responsibility, and that responsibility should be entirely encapsulated by the class. All its services should be narrowly aligned with that responsibility。如果在互联网上搜索“单一职责原则”,也会有非常多的文章,其核心内容为:“就一个类而言,应该只专注于做一件事和仅有一个引起它变化的原因。”,或者说“一个类应该有且只有一个变化的原因”。然而,在实际实践过程中,象是.net framework中的类库,它们是怎么体现单一责职责的呢,或者说是我们自己开发的程序,又是如何来应用单一平职原则呢?我经常看着周围世界里的事物在想,它们如果用面向对象去编程,又如何应用单一职责原则的,例如现实世界中的“人”这个类,如何在面向对象设计中进行单一职责。

  为了应用“单一责职”,我得先去搞清它的来龙去脉。根据维基百科的介绍,单一职责原则(Single Responsibility Principle,SRP)最初是由Bot大叔在文章PrinciplesOfOod中进行介绍的,详见易易部落对面向对象原则的介绍,及该原则的原始pdf文件,后来随他的这本《敏捷软件开发——原则、模式和实践》而风靡面向对象世界。我找到了该本书之后的另一本书《敏捷软件开发——原则、模式和实践 C#版》(中文版由人民邮电出版社于2008年1月出版),在第8章(第89页,英文书第115页)整章讲的就是 SRP:单一职责原则。该章的第一句话为“只有佛自己应当担负起公布玄妙秘密的职责……”,它说这条原则曾经在Tom DeMarco[DeMarco79],p.310和Meilir Page-Jones[Page-Jones88],p.82中有描述,也就是说如果追塑起来,就到了70年代和80年代了

  在该章中,定义了SRP:单一职责原则是“一个类应该只有一个发生变化的原因”,其中责职的定义为变化的原因,该章用了四页来介绍这个原则,然而,就算我认真的看了几篇,但还是有些搞不清楚,什么叫“变化的原因”,并且书中还有一个推论:仅当变化发生时,变化的轴线才具有实际意义。如果没有征兆,那么应用SRP或者任何其他原则都是不明智的。那么,什么又是“变化的轴线”呢?

  在结论中说SRP是所有原则中最简单的原则之一,也是最难正确运用的原则之一,并且该章的最后一句是这样说的事实上,我们将要论述的其余原则都会以这样或那样的方式回到这个问题上。又说明了该条原则的重要性。另外,在《代码整洁之道》p128页,也提到了SRP,并认为类或模块应有且只有一条加以修改的理由,在这一节也强调了SRP的重要性。并强调了类要短小

  除了上述正宗的描述,在网络上还可以找到很多有价值的关于单一责职原则的内容,例如瑞士军刀 VS 单一职责原则,也有根据单一职责原则喜欢将手机和相机功能分开来的。当然我不赞同手机相机功能分开,我希望手机相机摄像机都合在一起,并且一个设置在手就能达单反相面+专业摄像机+智能手机的水平。

  我希望未来的编程是能够遵循“本体”规则的,我也希望我能通过“本体”的理解来理解“单一职责原则”,特别上在上面提到的如果在现实世界中建立“人”这个类!也许大家会说,软件设计都是跟据领域来的,不同领域有不同的理解,首先确定领域才能够份析“人”这个类,那么,我就把这个领域想象成《阿凡达》游戏中的阿凡达或者《黑客帝国》中的主角。我现在要对里面的这个“人”建类。我该如何“单一职责”呢?(如果工作上真的让我单一职责,那我该多舒服呀?)

  我走路时想,睡觉时想,我想,做为“人”这个类,它会吃饭,会走路,会呼吸……功能实在太多!当然我也想到了“摘月亮”、“一巴掌就拍到拉登脸上”,“摇摇双手就能飞”等功能。软件世界里,一个类几乎可以拥有任何功能,我想,也正因为类(或者结构化中的函数)可以拥有太多的功能,特别是有了许多不该拥有的功能从而使得软件开发陷入了《人月神话》所描述的焦油坑中。然而,Bob大叔说的单一功能又感觉很难理解,然而我是“整洁代码派”(《代码整洁之道》P10)的拥护者,虽然我自己自立“本体派”,但我的“本体派”也得兼容它的单一职责原则。

  想着想着,我想说“人”这个类应该是拥有“思想”这个属性,和“满足欲望并活下去”这个方法的类。慢着!怎么会有一个“并”字呢,(参见《代码整洁之道》p128:我们应该用大概25个单词简要描述一个类,且不用“若(if)”、“与(and)”、“或(or)”或者“但(but)”等词汇。),有个“并”字就说明该类有两个职责,一个职责是“满足欲望”,另一个职责是“活下去”。那好,想想《狮子王》那道《生生不息》和“生生之为易”的大道理,那就将满足欲望放进活下去这个职责中,于是我拥有了一个单一职责的“人”类了!这个类的单一职责就是“活下去”。当然,在活下去这职责里又会涉及到很多的类和方法,如象上面提到的会吃饭的功能就会是嘴巴这个类的事,会呼吸就会是鼻子,会走路这个功能可能就要是脚相关的类了。再回头去理解“变化的原因”,那应该就是“环境”了,环境这个因素就是“人”这个类的变化的原因了,轴线也应该是它了。

  再回过头去看《敏捷软件开发》中的两个重构的例子,第一个是关于“矩形”类有两个方法,一个计算面积,一个画出矩形。接下去把它分解为两个类,一个矩形类,一个矩形结构类,分离了职责,应该还好理解。但第二个例子(详见书第91页,网上也有很多以此为例的),会不好理解,把Modem接口分解成两个接口然后继承之,使得ModemImplementation类成了个杂凑物,作者说“我们已经把丑陋部分隐藏起来了,其丑陋性不会泄漏出来。可是这时候ModemImplementation这个类是不是两个职责了呢?丑陋隐藏起来,它还是丑陋。为什么就不能让它也美丽起来呢?我按照本体概念,一个Modem应该是这样一个电子设备:插上电话线,连上电脑,具有拨通挂断功能,拨通后就能进行数据通信。它的单一功能是拨通挂断(我们可以将一组功能作为单一责职),而将数据通信用另外的类来表示。也就是让ModemImplementation拥有ModemDataChannel类(该类有一个开关属性),以及拨通挂断的方法,C#实现如下:

//该两类同样实现了书中介绍的接口和所有功能,关键是没了书中说的“被隐藏的丑陋”。
public class ModemDataChannel:DataChannel
{
  public bool IsChannelOn{get;set;}
  public void send(char c)
  {
    //实现发送
  }
  public void recv()
  {
    //实现接收
  }
}

public class ModemImplementation:Connection
{
  public DataChannel channel;
  public void Dial(string pno);
  {
    //实现Dial(string pno)功能
    channel.IsChannelOn=true;
  }
  public void Hangup()
  {
    //实现Hangup()功能
    channel.IsChannelOn=false;
  }
}

  这样的实现,不但实现了单一职责,也实现了两个接口,关键是,没有丑陋被隐藏起来!这样的实现是不是更好呢?再看那个瑞士军刀,我觉得它也单一功能,就是方便生活。而多功能手机也一样,满足我携带方便的功能,只是它拥有了许多的类,这些类分别有不同的单一职责而已。不知我这样的理解有没有出入,然望同行们评论!

  再将这个SRP单一职责问题同哲学连接起来,再想想我们人类自己,如果说我们只有单一责职,那么我们的单一职责是什么,是不是仅仅我上面说的“活下去”?再现看看一手摭天那些领导们,是不是权责太多了呀?

添加新评论

  • 自动将网址与电子邮件地址转变为链接。
  • 允许HTML标签:<a> <em> <strong> <cite> <blockquote> <code> <ul> <ol> <li> <dl> <dt> <dd>
  • 自动断行和分段。
  • No HTML tags allowed.
  • 自动将网址与电子邮件地址转变为链接。
  • 自动断行和分段。
Mollom CAPTCHA (play audio CAPTCHA)
Type the characters you see in the picture above; if you can't read them, submit the form and a new image will be generated. Not case sensitive.