面向对象程序设计原则(Principles of OOD)——SOLID

进行面向对象程序设计的时候,我们需要面对很多问题,比如:

  • 什么时候需要一个类
  • 一个类应该包含哪些功能,不包含哪些功能
  • 类之间的依赖关系设计

还有很多其它的问题,如果不遵照一些原则,而一切按照“本能”来的话,你的程序很快就会成为一坨不可靠,不容易扩展,不容易复用的“屎山”。面向对象设计原则就是为了保证你的代码可靠,易扩展,易复用而人为规定的一系列方法,它们是由大量资深的前辈们总结出来的经验。

这里先介绍一个经典的设计原则:SOLID

SRP The Single Responsibility Principle ''A class should have one, and only one, reason to change.''
OCP The Open Closed Principle You should be able to extend a classes behavior, without modifying it.
LSP The Liskov Substitution Principle Derived classes must be substitutable for their base classes.
ISP The Interface Segregation Principle Make fine grained interfaces that are client specific.
DIP The Dependency Inversion Principle Depend on abstractions, not on concretions.

这里先说一下SRP,参考文档,假设我们在实现一个游戏,需要计算矩形的面积,以及在GUI中显示矩形这两个功能,那么如果你很直接地实现一个Rectangle类,并同时让它实现计算面积跟GUI显示,那么就违反了SRP原则。

首先,如果我们用的是C++(或者java之类,Python不会有这个问题,它是通过解释器运行的,不用link)那么这个Rectangle类需要link GUI的库,相对于单纯只进行几何计算,需要额外的link和compile时间以及内存开销(java中需要加入GUI库的class文件)。其次,如果我们需要修改Rectangle的渲染部分代码,由于它跟计算部分写在同一个类里面,这会导致我们需要对计算部分也进行重编译以及测试等。

所以,一个遵循SRP的设计,应该是把计算逻辑跟渲染逻辑分别做在两个类里面,也就是“每个类只有一个职责”。那么问题来了,我们该如何去定义一个“职责”,也就是功能应该细分到什么粒度?无限细分显然是错误的,我们没有必要给每一个小功能定义一个类,而有时候看似合理的划分其实又是包含了多个职责的,原文给出的说明是:“A class should have one, and only one, reason to change.”看似很好理解,其实有时候也很具有迷惑性,这里有一个例子:
如果我们定义一个Modem类,它具有以下函数:

class Modem
{
  public void dial(String pno);
  public void hangup();
  public void send(char c);
  public char recv();
}

看上去似乎一切完美,但是它事实上是违背了SRP原则的,dialhangup属于连接管理功能,而sendrecv则是数据管理,也就是对于Modem接口,有超过一个以上的理由去修改。

我们可以把Modem拆分成DataChannelConnection两个接口,这样做似乎有点过于繁琐,并且这两个部分经常是需要同时使用的,所以我们可以使用一个ModemImplementation类继承这两个接口。

class DataChannel
{
  public virtual void send(char c) = 0;
  public virtual char recv() = 0;
}

class Connection
{
  public virtual void dial(String pno) = 0;
  public virtual void hangup() = 0;
}

class ModemImplementation : public DataChannel, public Connection
{
  public void send(char c);
  public char recv();
  public void dial(String pno);
  public void hangup();
}

也许看上去这很丑陋且冗余,但这经常是必要的,细分必然会导致繁琐不便(很多场合经常会需要同时使用多个细分功能),接口上细分,类实现再组合保留了便利性和SRP原则的益处,比如系统中有的模块如果需要data部分,那么它可以仅引入DataChannel,通过interface去调用ModemImplementation内部的数据部分功能,没有任何模块需要去依赖ModemImplementation,只有主函数需要知道ModemImplementation的存在。

总的来说,应用SRP原则时需要正确地去区分所谓的“responsibility”,这个没那么容易,同时出于方便考虑,在把功能细分以后重组有时也是必要的。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,839评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,543评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,116评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,371评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,384评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,111评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,416评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,053评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,558评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,007评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,117评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,756评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,324评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,315评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,539评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,578评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,877评论 2 345

推荐阅读更多精彩内容