定義
Software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification.
即:一個(gè)軟件實(shí)體如類(lèi)、模塊和函數(shù)應(yīng)該對(duì)擴(kuò)展開(kāi)放,對(duì)修改關(guān)閉蛮位。
定義的解讀
- 用抽象構(gòu)建框架,用實(shí)現(xiàn)擴(kuò)展細(xì)節(jié)葛账。
- 不以改動(dòng)原有類(lèi)的方式來(lái)實(shí)現(xiàn)新需求闷营,而是應(yīng)該以實(shí)現(xiàn)事先抽象出來(lái)的接口(或具體類(lèi)繼承抽象類(lèi))的方式來(lái)實(shí)現(xiàn)霞幅。
優(yōu)點(diǎn)
實(shí)踐開(kāi)閉原則的優(yōu)點(diǎn)在于可以在不改動(dòng)原有代碼的前提下給程序擴(kuò)展功能娩脾。增加了程序的可擴(kuò)展性争拐,同時(shí)也降低了程序的維護(hù)成本。
代碼講解
下面通過(guò)一個(gè)簡(jiǎn)單的關(guān)于在線(xiàn)課程的例子講解一下開(kāi)閉原則的實(shí)踐晦雨。
需求點(diǎn)
設(shè)計(jì)一個(gè)在線(xiàn)課程類(lèi):
由于教學(xué)資源有限,開(kāi)始的時(shí)候只有類(lèi)似于博客的隘冲,通過(guò)文字講解的課程闹瞧。 但是隨著教學(xué)資源的增多,后來(lái)增加了視頻課程展辞,音頻課程以及直播課程奥邮。
先來(lái)看一下不好的設(shè)計(jì):
不好的設(shè)計(jì)
最開(kāi)始的文字課程類(lèi):
//================== Course.h ==================
@interface Course : NSObject
@property (nonatomic, copy) NSString *courseTitle; //課程名稱(chēng)
@property (nonatomic, copy) NSString *courseIntroduction; //課程介紹
@property (nonatomic, copy) NSString *teacherName; //講師姓名
@property (nonatomic, copy) NSString *content; //課程內(nèi)容
@end
Course
類(lèi)聲明了最初的在線(xiàn)課程所需要包含的數(shù)據(jù):
- 課程名稱(chēng)
- 課程介紹
- 講師姓名
- 文字內(nèi)容
接著按照上面所說(shuō)的需求變更:增加了視頻,音頻罗珍,直播課程:
//================== Course.h ==================
@interface Course : NSObject
@property (nonatomic, copy) NSString *courseTitle; //課程名稱(chēng)
@property (nonatomic, copy) NSString *courseIntroduction; //課程介紹
@property (nonatomic, copy) NSString *teacherName; //講師姓名
@property (nonatomic, copy) NSString *content; //文字內(nèi)容
//新需求:視頻課程
@property (nonatomic, copy) NSString *videoUrl;
//新需求:音頻課程
@property (nonatomic, copy) NSString *audioUrl;
//新需求:直播課程
@property (nonatomic, copy) NSString *liveUrl;
@end
三種新增的課程都在原Course
類(lèi)中添加了對(duì)應(yīng)的url洽腺。也就是每次添加一個(gè)新的類(lèi)型的課程,都在原有Course
類(lèi)里面修改:新增這種課程需要的數(shù)據(jù)覆旱。
這就導(dǎo)致:我們從Course
類(lèi)實(shí)例化的視頻課程對(duì)象會(huì)包含并不屬于自己的數(shù)據(jù):audioUrl
和liveUrl
:這樣就造成了冗余蘸朋,視頻課程對(duì)象并不是純粹的視頻課程對(duì)象,它包含了音頻地址扣唱,直播地址等成員藕坯。
很顯然,這個(gè)設(shè)計(jì)不是一個(gè)好的設(shè)計(jì)噪沙,因?yàn)椋▽?duì)應(yīng)上面兩段敘述):
- 隨著需求的增加炼彪,需要反復(fù)修改之前創(chuàng)建的類(lèi)。
- 給新增的類(lèi)造成了不必要的冗余正歼。
之所以會(huì)造成上述兩個(gè)缺陷辐马,是因?yàn)樵撛O(shè)計(jì)沒(méi)有遵循對(duì)修改關(guān)閉,對(duì)擴(kuò)展開(kāi)放的開(kāi)閉原則局义,而是反其道而行之:開(kāi)放修改喜爷,而且不給擴(kuò)展提供便利冗疮。
難么怎么做可以遵循開(kāi)閉原則呢?下面看一下遵循開(kāi)閉原則的較好的設(shè)計(jì):
較好的設(shè)計(jì)
首先在Course
類(lèi)中僅僅保留所有課程都含有的數(shù)據(jù):
//================== Course.h ==================
@interface Course : NSObject
@property (nonatomic, copy) NSString *courseTitle; //課程名稱(chēng)
@property (nonatomic, copy) NSString *courseIntroduction; //課程介紹
@property (nonatomic, copy) NSString *teacherName; //講師姓名
接著贞奋,針對(duì)文字課程赌厅,視頻課程,音頻課程轿塔,直播課程這三種新型的課程采用繼承Course
類(lèi)的方式特愿。而且繼承后,添加自己獨(dú)有的數(shù)據(jù):
文字課程類(lèi):
//================== TextCourse.h ==================
@interface TextCourse : Course
@property (nonatomic, copy) NSString *content; //文字內(nèi)容
@end
視頻課程類(lèi):
//================== VideoCourse.h ==================
@interface VideoCourse : Course
@property (nonatomic, copy) NSString *videoUrl; //視頻地址
@end
音頻課程類(lèi):
//================== AudioCourse.h ==================
@interface AudioCourse : Course
@property (nonatomic, copy) NSString *audioUrl; //音頻地址
@end
直播課程類(lèi):
//================== LiveCourse.h ==================
@interface LiveCourse : Course
@property (nonatomic, copy) NSString *liveUrl; //直播地址
@end
這樣一來(lái)勾缭,上面的兩個(gè)問(wèn)題都得到了解決:
- 隨著課程類(lèi)型的增加揍障,不需要反復(fù)修改最初的父類(lèi)(
Course
),只需要新建一個(gè)繼承于它的子類(lèi)并在子類(lèi)中添加僅屬于該子類(lèi)的數(shù)據(jù)(或行為)即可俩由。 - 因?yàn)楦鞣N課程獨(dú)有的數(shù)據(jù)(或行為)都被分散到了不同的課程子類(lèi)里毒嫡,所以每個(gè)子類(lèi)的數(shù)據(jù)(或行為)沒(méi)有任何冗余。
而且對(duì)于第二點(diǎn):或許今后的視頻課程可以有高清地址幻梯,視頻加速功能兜畸。而這些功能只需要在VideoCourse
類(lèi)里添加即可,因?yàn)樗鼈兌际且曨l課程所獨(dú)有的碘梢。同樣地咬摇,直播課程后面還可以支持在線(xiàn)問(wèn)答功能,也可以?xún)H加在LiveCourse
里面煞躬。
我們可以看到肛鹏,正是由于最初程序設(shè)計(jì)合理,所以對(duì)后面需求的增加才會(huì)處理得很好恩沛。
下面來(lái)看一下這兩個(gè)設(shè)計(jì)的UML 類(lèi)圖在扰,可以更形象地看出兩種設(shè)計(jì)上的區(qū)別:
UML 類(lèi)圖對(duì)比
未實(shí)踐開(kāi)閉原則:
實(shí)踐了開(kāi)閉原則:
在實(shí)踐了開(kāi)閉原則的 UML 類(lèi)圖中,四個(gè)課程類(lèi)繼承了
Course
類(lèi)并添加了自己獨(dú)有的屬性雷客。(在 UML 類(lèi)圖中:實(shí)線(xiàn)空心三角箭頭代表繼承關(guān)系:由子類(lèi)指向其父類(lèi))
如何實(shí)踐
為了更好地實(shí)踐開(kāi)閉原則芒珠,在設(shè)計(jì)之初就要想清楚在該場(chǎng)景里哪些數(shù)據(jù)(或行為)是一定不變(或很難再改變)的,哪些是很容易變動(dòng)的佛纫。將后者抽象成接口或抽象方法妓局,以便于在將來(lái)通過(guò)創(chuàng)造具體的實(shí)現(xiàn)應(yīng)對(duì)不同的需求。