六大設(shè)計(jì)原則之單一職責(zé)原則(Single Responsibility Principle)

定義

A class should have a single responsibility, where a responsibility is nothing but a reason to change.

即:一個(gè)類只允許有一個(gè)職責(zé)蜒程,即只有一個(gè)導(dǎo)致該類變更的原因媚污。

定義的解讀

  • 類職責(zé)的變化往往就是導(dǎo)致類變化的原因:也就是說(shuō)如果一個(gè)類具有多種職責(zé)审姓,就會(huì)有多種導(dǎo)致這個(gè)類變化的原因,從而導(dǎo)致這個(gè)類的維護(hù)變得困難届谈。

  • 往往在軟件開發(fā)中隨著需求的不斷增加,可能會(huì)給原來(lái)的類添加一些本來(lái)不屬于它的一些職責(zé)弯汰,從而違反了單一職責(zé)原則艰山。如果我們發(fā)現(xiàn)當(dāng)前類的職責(zé)不僅僅有一個(gè),就應(yīng)該將本來(lái)不屬于該類真正的職責(zé)分離出去咏闪。

  • 不僅僅是類程剥,函數(shù)(方法)也要遵循單一職責(zé)原則,即:一個(gè)函數(shù)(方法)只做一件事情汤踏。如果發(fā)現(xiàn)一個(gè)函數(shù)(方法)里面有不同的任務(wù)织鲸,則需要將不同的任務(wù)以另一個(gè)函數(shù)(方法)的形式分離出去。

優(yōu)點(diǎn)

如果類與方法的職責(zé)劃分得很清晰溪胶,不但可以提高代碼的可讀性搂擦,更實(shí)際性地更降低了程序出錯(cuò)的風(fēng)險(xiǎn),因?yàn)榍逦拇a會(huì)讓bug無(wú)處藏身哗脖,也有利于bug的追蹤瀑踢,也就是降低了程序的維護(hù)成本扳还。

代碼講解

單一職責(zé)原則的demo比較簡(jiǎn)單,通過(guò)對(duì)象(屬性)的設(shè)計(jì)上講解已經(jīng)足夠橱夭,不需要具體的客戶端調(diào)用氨距。我們先看一下需求點(diǎn):

需求點(diǎn)

初始需求:需要?jiǎng)?chuàng)造一個(gè)員工類,這個(gè)類有員工的一些基本信息棘劣。

新需求:增加兩個(gè)方法:

  • 判定員工在今年是否升職
  • 計(jì)算員工的薪水

先來(lái)看一下不好的設(shè)計(jì):

不好的設(shè)計(jì)

//================== Employee.h ==================

@interface Employee : NSObject

//============ 初始需求 ============
@property (nonatomic, copy) NSString *name;       //員工姓名
@property (nonatomic, copy) NSString *address;    //員工住址
@property (nonatomic, copy) NSString *employeeID; //員工ID

//============ 新需求 ============
//計(jì)算薪水
- (double)calculateSalary;

//今年是否晉升
- (BOOL)willGetPromotionThisYear;

@end

由上面的代碼可以看出:

  • 在初始需求下俏让,我們創(chuàng)建了Employee這個(gè)員工類,并聲明了3個(gè)員工信息的屬性:?jiǎn)T工姓名茬暇,地址首昔,員工ID。
  • 在新需求下糙俗,兩個(gè)方法直接加到了員工類里面勒奇。

新需求的做法看似沒(méi)有問(wèn)題,因?yàn)槎际呛蛦T工有關(guān)的巧骚,但卻違反了單一職責(zé)原則:因?yàn)檫@兩個(gè)方法并不是員工本身的職責(zé)赊颠。

  • calculateSalary這個(gè)方法的職責(zé)是屬于會(huì)計(jì)部門的:薪水的計(jì)算是會(huì)計(jì)部門負(fù)責(zé)。
  • willPromotionThisYear這個(gè)方法的職責(zé)是屬于人事部門的:考核與晉升機(jī)制是人事部門負(fù)責(zé)劈彪。

而上面的設(shè)計(jì)將本來(lái)不屬于員工自己的職責(zé)強(qiáng)加進(jìn)了員工類里面巨税,而這個(gè)類的設(shè)計(jì)初衷(原始職責(zé))就是單純地保留員工的一些信息而已。因此這么做就是給這個(gè)類引入了新的職責(zé)粉臊,故此設(shè)計(jì)違反了單一職責(zé)原則草添。

我們可以簡(jiǎn)單想象一下這么做的后果是什么:如果員工的晉升機(jī)制變了,或者稅收政策等影響員工工資的因素變了扼仲,我們還需要修改當(dāng)前這個(gè)類远寸。

那么怎么做才能不違反單一職責(zé)原則呢?- 我們需要將這兩個(gè)方法(責(zé)任)分離出去屠凶,讓本應(yīng)該處理這類任務(wù)的類來(lái)處理驰后。

較好的設(shè)計(jì)

我們保留員工類的基本信息:

//================== Employee.h ==================

@interface Employee : NSObject

//初始需求
@property (nonatomic, copy) NSString *name;
@property (nonatomic, copy) NSString *address;
@property (nonatomic, copy) NSString *employeeID;

接著創(chuàng)建新的會(huì)計(jì)部門類:

//================== FinancialApartment.h ==================

#import "Employee.h"

//會(huì)計(jì)部門類
@interface FinancialApartment : NSObject

//計(jì)算薪水
- (double)calculateSalary:(Employee *)employee;

@end

人事部門類:

//================== HRApartment.h ==================

#import "Employee.h"

//人事部門類
@interface HRApartment : NSObject

//今年是否晉升
- (BOOL)willGetPromotionThisYear:(Employee*)employee;

@end

通過(guò)創(chuàng)建了兩個(gè)分別專門處理薪水和晉升的部門,會(huì)計(jì)部門和人事部門的類:FinancialApartmentHRApartment矗愧,把兩個(gè)任務(wù)(責(zé)任)分離了出去灶芝,讓本該處理這些職責(zé)的類來(lái)處理這些職責(zé)。

這樣一來(lái)唉韭,不僅僅在此次新需求中滿足了單一職責(zé)原則夜涕,以后如果還要增加人事部門和會(huì)計(jì)部門處理的任務(wù),就可以直接在這兩個(gè)類里面添加即可属愤。

下面來(lái)看一下這兩個(gè)設(shè)計(jì)的UML 類圖女器,可以更形象地看出兩種設(shè)計(jì)上的區(qū)別:

UML 類圖對(duì)比

未實(shí)踐單一職責(zé)原則:

實(shí)踐了單一職責(zé)原則:

可以看到,在實(shí)踐了單一職責(zé)原則的 UML 類圖中住诸,不屬于Employee的兩個(gè)職責(zé)被分類了FinancialApartment類 和 HRApartment類驾胆。(在 UML 類圖中涣澡,虛線箭頭表示依賴關(guān)系,常用在方法參數(shù)等丧诺,由依賴方指向被依賴方)

上面說(shuō)過(guò)除了類要遵循單一職責(zé)設(shè)計(jì)原則之外入桂,在函數(shù)(方法)的設(shè)計(jì)上也要遵循單一職責(zé)的設(shè)計(jì)原則。因函數(shù)(方法)的單一職責(zé)原則理解起來(lái)比較容易驳阎,故在這里就不提供Demo和UML 類圖了抗愁。

可以簡(jiǎn)單舉一個(gè)例子:

APP的默認(rèn)導(dǎo)航欄的樣式是這樣的:

  • 白色底
  • 黑色標(biāo)題
  • 底部有陰影

那么創(chuàng)建默認(rèn)導(dǎo)航欄的偽代碼可能是這樣子的:

//默認(rèn)樣式的導(dǎo)航欄
- (void)createDefaultNavigationBarWithTitle:(NSString *)title{

    //create white color background view

    //create black color title

    //create shadow bottom
}

現(xiàn)在我們可以用這個(gè)方法統(tǒng)一創(chuàng)建默認(rèn)的導(dǎo)航欄了。 但是過(guò)不久又有新的需求來(lái)了搞隐,有的頁(yè)面的導(dǎo)航欄需要做成透明的,因此需要一個(gè)透明樣式的導(dǎo)航欄:

  • 透明底
  • 白色標(biāo)題
  • 底部無(wú)陰影

針對(duì)這個(gè)需求远搪,我們可以新增一個(gè)方法:

//透明樣式的導(dǎo)航欄
- (void)createTransParentNavigationBarWithTitle:(NSString *)title{

    //create transparent color background view

    //create white color title
}

看出問(wèn)題來(lái)了么劣纲?在這兩個(gè)方法里面,創(chuàng)造background view和 title color title的方法的差別僅僅是顏色不同而已谁鳍,而其他部分的代碼是重復(fù)的癞季。 因此我們應(yīng)該將這兩個(gè)方法抽出來(lái):

//根據(jù)傳入的顏色參數(shù)設(shè)置導(dǎo)航欄的背景色
- (void)createBackgroundViewWithColor:(UIColor)color;

//根據(jù)傳入的標(biāo)題字符串和顏色參數(shù)設(shè)置標(biāo)題
- (void)createTitlewWithColorWithTitle:(NSString *)title color:(UIColor)color;

而且上面的制造陰影的部分也可以作為方法抽出來(lái):

- (void)createShadowBottom;

這樣一來(lái),原來(lái)的兩個(gè)方法可以寫成:

//默認(rèn)樣式的導(dǎo)航欄
- (void)createDefaultNavigationBarWithTitle:(NSString *)title{

    //設(shè)置白色背景
    [self createBackgroundViewWithColor:[UIColor whiteColor]];

    //設(shè)置黑色標(biāo)題
    [self createTitlewWithColorWithTitle:title color:[UIColor blackColor]];

    //設(shè)置底部陰影
    [self createShadowBottom];
}

//透明樣式的導(dǎo)航欄
- (void)createTransParentNavigationBarWithTitle:(NSString *)title{

    //設(shè)置透明背景
    [self createBackgroundViewWithColor:[UIColor clearColor]];

    //設(shè)置白色標(biāo)題
    [self createTitlewWithColorWithTitle:title color:[UIColor whiteColor]];
}

而且我們也可以將里面的方法拿出來(lái)在外面調(diào)用也可以:

設(shè)置默認(rèn)樣式的導(dǎo)航欄:

//設(shè)置白色背景
[navigationBar createBackgroundViewWithColor:[UIColor whiteColor]];

//設(shè)置黑色標(biāo)題
[navigationBar createTitlewWithColorWithTitle:title color:[UIColor blackColor]];

//設(shè)置陰影
[navigationBar createShadowBottom];

設(shè)置透明樣式的導(dǎo)航欄:

//設(shè)置透明色背景
[navigationBar createBackgroundViewWithColor:[UIColor clearColor]];

//設(shè)置白色標(biāo)題
[navigationBar createTitlewWithColorWithTitle:title color:[UIColor whiteColor]];

這樣一來(lái)倘潜,無(wú)論寫在一個(gè)大方法里面調(diào)用或是分別在外面調(diào)用绷柒,都能很清楚地看到導(dǎo)航欄的每個(gè)元素是如何生成的,因?yàn)槊總€(gè)職責(zé)都分配到了一個(gè)單獨(dú)的方法里面涮因。而且還有一個(gè)好處是废睦,透明導(dǎo)航欄如果遇到淺色背景的話,使用白色字體不如使用黑色字體好养泡,所以遇到這種情況我們可以在createTitlewWithColorWithTitle:color:方法里面?zhèn)魅牒谏怠?而且今后可能還會(huì)有更多的導(dǎo)航欄樣式嗜湃,那么我們只需要分別改變傳入的色值即可,不需要有大量的重復(fù)代碼了澜掩,修改起來(lái)也很方便购披。

如何實(shí)踐

對(duì)于上面的員工類的例子,或許是因?yàn)槲覀兿热霝橹骷玳牛酪粋€(gè)公司的合理組織架構(gòu)刚陡,覺得這么設(shè)計(jì)理所當(dāng)然。但是在實(shí)際開發(fā)中株汉,我們很容易會(huì)將不同的責(zé)任揉在一起筐乳,這點(diǎn)還是需要開發(fā)者注意的。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末乔妈,一起剝皮案震驚了整個(gè)濱河市哥童,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌褒翰,老刑警劉巖贮懈,帶你破解...
    沈念sama閱讀 211,265評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件匀泊,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡朵你,警方通過(guò)查閱死者的電腦和手機(jī)各聘,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,078評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)抡医,“玉大人躲因,你說(shuō)我怎么就攤上這事〖缮担” “怎么了大脉?”我有些...
    開封第一講書人閱讀 156,852評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)水孩。 經(jīng)常有香客問(wèn)我镰矿,道長(zhǎng),這世上最難降的妖魔是什么俘种? 我笑而不...
    開封第一講書人閱讀 56,408評(píng)論 1 283
  • 正文 為了忘掉前任秤标,我火速辦了婚禮,結(jié)果婚禮上宙刘,老公的妹妹穿的比我還像新娘苍姜。我一直安慰自己,他們只是感情好悬包,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,445評(píng)論 5 384
  • 文/花漫 我一把揭開白布衙猪。 她就那樣靜靜地躺著,像睡著了一般布近。 火紅的嫁衣襯著肌膚如雪屈嗤。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,772評(píng)論 1 290
  • 那天吊输,我揣著相機(jī)與錄音饶号,去河邊找鬼。 笑死季蚂,一個(gè)胖子當(dāng)著我的面吹牛茫船,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播扭屁,決...
    沈念sama閱讀 38,921評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼算谈,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了料滥?” 一聲冷哼從身側(cè)響起然眼,我...
    開封第一講書人閱讀 37,688評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎葵腹,沒(méi)想到半個(gè)月后高每,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體屿岂,經(jīng)...
    沈念sama閱讀 44,130評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,467評(píng)論 2 325
  • 正文 我和宋清朗相戀三年鲸匿,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了爷怀。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,617評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡带欢,死狀恐怖运授,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情乔煞,我是刑警寧澤吁朦,帶...
    沈念sama閱讀 34,276評(píng)論 4 329
  • 正文 年R本政府宣布,位于F島的核電站渡贾,受9級(jí)特大地震影響逗宜,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜剥啤,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,882評(píng)論 3 312
  • 文/蒙蒙 一锦溪、第九天 我趴在偏房一處隱蔽的房頂上張望不脯。 院中可真熱鬧府怯,春花似錦、人聲如沸防楷。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,740評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)复局。三九已至冲簿,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間亿昏,已是汗流浹背峦剔。 一陣腳步聲響...
    開封第一講書人閱讀 31,967評(píng)論 1 265
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留角钩,地道東北人吝沫。 一個(gè)月前我還...
    沈念sama閱讀 46,315評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像递礼,于是被迫代替她去往敵國(guó)和親惨险。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,486評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容