淺談面向?qū)ο蟮牧笤O(shè)計(jì)原則

image.png

原則一钙姊、單一職責(zé)原則(Single Responsibility Principle毯辅,簡(jiǎn)稱(chēng)SRP )

定義:應(yīng)該有且僅有一個(gè)原因引起類(lèi)的變更。

一個(gè)類(lèi)只負(fù)責(zé)一項(xiàng)職責(zé)煞额,如果發(fā)生變更時(shí)思恐,可以考慮將一個(gè)類(lèi)拆分成兩個(gè)類(lèi),或者在一個(gè)類(lèi)中添加新的方法立镶。

在真實(shí)的開(kāi)發(fā)中壁袄,不僅僅是類(lèi)类早、函數(shù)和接口也要遵循單一職責(zé)原則媚媒。即:一個(gè)函數(shù)負(fù)責(zé)一個(gè)功能。如果一個(gè)函數(shù)里面有不同的功能涩僻,則需要將不同的功能的函數(shù)分離出去缭召。

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

  • 類(lèi)的復(fù)雜性降低,實(shí)現(xiàn)什么職責(zé)都有清晰明確的定義逆日。
  • 類(lèi)的可讀性提高嵌巷,復(fù)雜性減低。

如果接口或者函數(shù)的單一職責(zé)做得好室抽,一個(gè)接口或者函數(shù)的修改只對(duì)相應(yīng)的類(lèi)有影響搪哪,對(duì)其他接口或者函數(shù)無(wú)影響,這對(duì)系統(tǒng)的擴(kuò)展性坪圾、維護(hù)性都有非常大的幫助晓折。

例如,需求上指出用一個(gè)類(lèi)描述食肉和食草動(dòng)物:

//================== Animal.h ==================

@interface Animal : NSObject

- (void)eatWithAnimalName:(NSString *)animalName;

@end

運(yùn)行結(jié)果:

2018-10-27 17:55:25.775317+0800 DesignPatterns[54087:24701786] 狼 吃肉
2018-10-27 17:55:25.775689+0800 DesignPatterns[54087:24701786] 豹 吃肉
2018-10-27 17:55:25.775721+0800 DesignPatterns[54087:24701786] 虎 吃肉

上線(xiàn)后兽泄,發(fā)現(xiàn)問(wèn)題了漓概,并不是所有的動(dòng)物都是吃肉的,比如羊就是吃草的病梢。修改時(shí)如果遵循單一職責(zé)原則胃珍,需要將 Animal 類(lèi)細(xì)分為食草動(dòng)物類(lèi) Herbivore,食肉動(dòng)物 Carnivore蜓陌,代碼如下:

//================== Herbivore.h ==================
@interface Herbivore : Animal

@end

@implementation Herbivore

- (void)eatWithAnimalName:(NSString *)animalName {
    NSLog(@"%@ 吃草", animalName);
}

@end

//================== Carnivore.h ==================
@interface Carnivore : Animal

@end

@implementation Carnivore

- (void)eatWithAnimalName:(NSString *)animalName {
    NSLog(@"%@ 吃肉", animalName);
}

@end

//================== main 函數(shù) ==================
Animal *carnivore = [Carnivore new];
[carnivore eatWithAnimalName:@"狼"];
[carnivore eatWithAnimalName:@"豹"];
[carnivore eatWithAnimalName:@"虎"];
NSLog(@"\n");
Animal *herbivore = [Herbivore new];
[herbivore eatWithAnimalName:@"羊"];

在子類(lèi)里面重寫(xiě)父類(lèi)的 eatWithAnimalName 函數(shù)觅彰,運(yùn)行結(jié)果:

2018-10-27 18:04:49.189722+0800 DesignPatterns[54422:24725132] 狼 吃肉
2018-10-27 18:04:49.190450+0800 DesignPatterns[54422:24725132] 豹 吃肉
2018-10-27 18:04:49.190482+0800 DesignPatterns[54422:24725132] 虎 吃肉
2018-10-27 18:04:49.190498+0800 DesignPatterns[54422:24725132] 
2018-10-27 18:04:49.190530+0800 DesignPatterns[54422:24725132] 羊 吃草

這樣一來(lái),不僅僅在此次新需求中滿(mǎn)足了單一職責(zé)原則钮热,以后如果還要增加食肉動(dòng)物和食草動(dòng)物的其他功能缔莲,就可以直接在這兩個(gè)類(lèi)里面添加即可。但是霉旗,有一點(diǎn)痴奏,修改花銷(xiāo)是很大的蛀骇,除了將原來(lái)的類(lèi)分解之外,還需要修改 main 函數(shù) 读拆。而直接修改類(lèi) Animal 來(lái)達(dá)成目的雖然違背了單一職責(zé)原則擅憔,但花銷(xiāo)卻小的多,代碼如下:

//================== Animal.h ==================

@interface Animal : NSObject

- (void)eatWithAnimalName:(NSString *)animalName;

@end

@implementation Animal

- (void)eatWithAnimalName:(NSString *)animalName {
    if ([@"羊" isEqualToString:animalName]) {
        NSLog(@"%@ 吃草", animalName);
    } else {
        NSLog(@"%@ 吃肉", animalName);
    }
}

@end

//================== main 函數(shù) ==================

Animal *animal = [Animal new];
[animal eatWithAnimalName:@"狼"];
[animal eatWithAnimalName:@"豹"];
[animal eatWithAnimalName:@"虎"];
[animal eatWithAnimalName:@"羊"];

運(yùn)行結(jié)果:

2018-10-27 18:16:10.910397+0800 DesignPatterns[54677:24751636] 狼 吃肉
2018-10-27 18:16:10.911105+0800 DesignPatterns[54677:24751636] 豹 吃肉
2018-10-27 18:16:10.911138+0800 DesignPatterns[54677:24751636] 虎 吃肉
2018-10-27 18:16:10.911160+0800 DesignPatterns[54677:24751636] 羊 吃草

可以看到檐晕,這種修改方式要簡(jiǎn)單的多暑诸。
但是卻存在著隱患:有一天需求上增加牛和馬也需要吃草,則又需要修改 Animal 類(lèi)的 eatWithAnimalName 函數(shù)辟灰,而對(duì)原有代碼的修改會(huì)對(duì)調(diào)用狼个榕、豹和虎吃肉等功能帶來(lái)風(fēng)險(xiǎn),也許某一天你會(huì)發(fā)現(xiàn)運(yùn)行結(jié)果變?yōu)榛⒁渤圆萘恕?/strong>這種修改方式直接在代碼級(jí)別上違背了單一職責(zé)原則芥喇,雖然修改起來(lái)最簡(jiǎn)單西采,但隱患卻是最大的。還有一種修改方式:

//================== Animal.h ==================

@interface Animal : NSObject

/**
 *  吃草
 */
- (void)eatGrassWithAnimalName:(NSString *)animalName;

/**
 *  吃肉
 */
- (void)eatMeatWithAnimalName:(NSString *)animalName;

@end

@implementation Animal

- (void)eatGrassWithAnimalName:(NSString *)animalName {
    NSLog(@"%@ 吃草", animalName);
}

- (void)eatMeatWithAnimalName:(NSString *)animalName {
    NSLog(@"%@ 吃肉", animalName);
}

@end

//================== main 函數(shù) ==================

Animal *animal = [Animal new];
[animal eatMeatWithAnimalName:@"狼"];
[animal eatMeatWithAnimalName:@"豹"];
[animal eatMeatWithAnimalName:@"虎"];
[animal eatGrassWithAnimalName:@"羊"];

運(yùn)行結(jié)果:

2018-10-27 18:31:30.321473+0800 DesignPatterns[55048:24787008] 狼 吃肉
2018-10-27 18:31:30.321884+0800 DesignPatterns[55048:24787008] 豹 吃肉
2018-10-27 18:31:30.321922+0800 DesignPatterns[55048:24787008] 虎 吃肉
2018-10-27 18:31:30.321939+0800 DesignPatterns[55048:24787008] 羊 吃草

通過(guò)運(yùn)行結(jié)果可以看到继控,這種修改方式?jīng)]有改動(dòng)原來(lái)的函數(shù)械馆,而是在類(lèi)中新加了一個(gè)函數(shù),這樣雖然也違背了類(lèi)單一職責(zé)原則武通,但在函數(shù)級(jí)別上卻是符合單一職責(zé)原則的霹崎,因?yàn)樗](méi)有動(dòng)原來(lái)函數(shù)的代碼。

在實(shí)際的開(kāi)發(fā)應(yīng)用中冶忱,有很多復(fù)雜的場(chǎng)景尾菇,怎么設(shè)計(jì)一個(gè)類(lèi)或者一個(gè)函數(shù),讓?xiě)?yīng)用程序更加靈活囚枪,是更多程序員們值得思考的派诬,需要結(jié)合特定的需求場(chǎng)景,有可能有些類(lèi)里面有很多的功能眶拉,但是切記不要將不屬于這個(gè)類(lèi)本身的功能也強(qiáng)加進(jìn)來(lái)千埃,這樣不僅帶來(lái)不必要的維護(hù)成本,也違反了單一職責(zé)的設(shè)計(jì)原則忆植。

原則二放可、里氏替換原則(Liskov Substitution Principle,簡(jiǎn)稱(chēng)LSP)

定義:如果對(duì)一個(gè)類(lèi)型為 T1 的對(duì)象 o1朝刊,都有類(lèi)型為 T2 的對(duì)象 o2耀里,使得以 T1 定義的所有程序 P 在所有的對(duì)象 o1 都替換成 o2 時(shí),程序 P 的行為沒(méi)有發(fā)生變化拾氓,那么類(lèi)型 T2 是類(lèi)型 T1 的子類(lèi)型冯挎。有點(diǎn)拗口,通俗點(diǎn)講咙鞍,只要父類(lèi)能出現(xiàn)的地方子類(lèi)就可以出現(xiàn)房官,而且替換為子類(lèi)也不會(huì)產(chǎn)生任何錯(cuò)誤或異常趾徽,使用者不需要知道是父類(lèi)還是子類(lèi)。但是翰守,反過(guò)來(lái)就不行了孵奶,有子類(lèi)出現(xiàn)的地方,父類(lèi)未必就能適應(yīng)蜡峰。

面向?qū)ο蟮恼Z(yǔ)言的三大特點(diǎn)是繼承了袁、封裝、多態(tài)湿颅,里氏替換原則就是依賴(lài)于繼承载绿、多態(tài)這兩大特性。當(dāng)使用繼承時(shí)油航,遵循里氏替換原則崭庸。但是使用繼承會(huì)給程序帶來(lái)侵入性,程序的可移植性降低劝堪,增加了對(duì)象間的耦合性冀自,如果一個(gè)類(lèi)被其他的類(lèi)所繼承揉稚,則當(dāng)這個(gè)類(lèi)需要修改時(shí)秒啦,必須考慮到所有的子類(lèi),并且父類(lèi)修改后搀玖,所有涉及到子類(lèi)的功能都有可能會(huì)產(chǎn)生影響余境。子類(lèi)可以擴(kuò)展父類(lèi)的功能,但不能改變父類(lèi)原有的功能灌诅。

注意:

  • 子類(lèi)可以實(shí)現(xiàn)父類(lèi)的抽象方法芳来,但不能覆蓋父類(lèi)的非抽象方法。
  • 子類(lèi)中可以增加自己特有的方法猜拾。
  • 當(dāng)子類(lèi)的方法重載父類(lèi)的方法時(shí)即舌,方法的前置條件(即方法的形參)要比父類(lèi)方法的輸入?yún)?shù)更寬松。
  • 當(dāng)子類(lèi)的方法實(shí)現(xiàn)父類(lèi)的抽象方法時(shí)挎袜,方法的后置條件(即方法的返回值)要比父類(lèi)更嚴(yán)格顽聂。

比如,需要完成一個(gè)兩數(shù)相加的功能:

//================== A.h ==================

@interface A : NSObject

/**
 加法

 @param a
 @param b
 @return 相加之后的和
 */
- (NSInteger)addition:(NSInteger)a b:(NSInteger)b;

@end

//================== main 函數(shù) ==================

A *a = [[A alloc] init];
NSLog(@"100+50=%ld", [a addition:100 b:50]);
NSLog(@"100+80=%ld", [a addition:100 b:80]);

運(yùn)行結(jié)果如下盯仪,

2018-11-01 22:53:23.549358+0800 DesignPatterns[18063:363232] 100+50=150
2018-11-01 22:53:23.549586+0800 DesignPatterns[18063:363232] 100+80=180

接著紊搪,需求上需要增加一個(gè)新的功能,完成兩數(shù)相加全景,然后再與 100 求差耀石,由類(lèi) B 來(lái)負(fù)責(zé)。即類(lèi) B 需要完成兩個(gè)功能:

  • 兩數(shù)相減爸黄。
  • 兩數(shù)相加滞伟,然后再加 100揭鳞。

由于類(lèi) A 已經(jīng)實(shí)現(xiàn)了加法功能,所以 B 繼承 A 之后梆奈,只需要完成減法功能就可以了汹桦,但是在類(lèi) B 中不小心重寫(xiě)了父類(lèi) A 的減法功能,如下:

//================== B.h ==================

@interface B : A

/**
 加法
 
 @param a
 @param b
 @return 相加之后的和
 */
- (NSInteger)addition:(NSInteger)a b:(NSInteger)b;


/**
 減法
 
 @param a
 @param b
 @return 相加之后的和
 */
- (NSInteger)subtraction:(NSInteger)a b:(NSInteger)b;

@end

//================== main 函數(shù) ==================

B *b = [[B alloc] init];
NSInteger sub = [b addition:100 b:50];
NSInteger difference = [b subtraction:sub b:100];
NSLog(@"100+50=%ld", sub);
NSLog(@"100+100+50=%ld", difference);

運(yùn)行結(jié)果如下鉴裹,

2018-11-01 23:15:06.530080+0800 DesignPatterns[18363:375940] 100+50=5000
2018-11-01 23:15:06.530758+0800 DesignPatterns[18363:375940] 100+100+50=4900

發(fā)現(xiàn)原本運(yùn)行正常的相減功能發(fā)生了錯(cuò)誤舞骆,原因就是類(lèi) B 在給方法起名時(shí)無(wú)意中重寫(xiě)了父類(lèi)的方法,造成所有運(yùn)行相減功能的代碼全部調(diào)用了類(lèi) B 重寫(xiě)后的方法径荔,造成原本運(yùn)行正常的功能出現(xiàn)了錯(cuò)誤督禽。如果按照“里氏替換原則”,只要父類(lèi)能出現(xiàn)的地方子類(lèi)就可以出現(xiàn)总处,而且替換為子類(lèi)也不會(huì)產(chǎn)生任何錯(cuò)誤或異常狈惫,使用者不需要知道是父類(lèi)還是子類(lèi),是不成立的鹦马。

在平時(shí)的日常開(kāi)發(fā)中胧谈,通常會(huì)通過(guò)重寫(xiě)父類(lèi)的方法來(lái)完成新的功能,這樣寫(xiě)起來(lái)雖然簡(jiǎn)單荸频,但是整個(gè)繼承體系的可復(fù)用性會(huì)比較差菱肖,特別是運(yùn)用多態(tài)比較頻繁時(shí),程序運(yùn)行出錯(cuò)的幾率非常大旭从。

原則三稳强、依賴(lài)倒置原則(Dependence Inversion Principle,簡(jiǎn)稱(chēng)DIP)

依賴(lài)倒置原則的核心思想是面向接口編程和悦。

定義:模塊間的依賴(lài)通過(guò)抽象發(fā)生退疫,高層模塊和低層模塊之間不應(yīng)該發(fā)生直接的依賴(lài)關(guān)系,二者都應(yīng)該是通過(guò)接口或抽象類(lèi)產(chǎn)生的鸽素;即依賴(lài)抽象褒繁,而不依賴(lài)具體的實(shí)現(xiàn)。

例如:類(lèi) A 直接依賴(lài)類(lèi) B馍忽,假如要將類(lèi) A 改為依賴(lài)類(lèi) C棒坏,則必須通過(guò)修改類(lèi) A 的代碼來(lái)達(dá)成。比如在這種場(chǎng)景下舵匾,業(yè)務(wù)邏輯層類(lèi) A 相對(duì)于數(shù)據(jù)層類(lèi) B 是高層模塊俊抵,因?yàn)闃I(yè)務(wù)邏輯層需要調(diào)用數(shù)據(jù)層去連接數(shù)據(jù)庫(kù),如果業(yè)務(wù)邏輯層類(lèi) A 依賴(lài)數(shù)據(jù)層類(lèi) B 的話(huà)坐梯,那么將來(lái)需求變更徽诲,需要把舊的數(shù)據(jù)層類(lèi) B 修改為新的數(shù)據(jù)層類(lèi) C,就必須通過(guò)修改類(lèi) A,這樣就會(huì)給應(yīng)用程序帶來(lái)不必要的風(fēng)險(xiǎn)谎替。

解決方案:將類(lèi) A 修改為依賴(lài)接口 I偷溺,類(lèi) B 和類(lèi) C 各自實(shí)現(xiàn)接口 I,類(lèi) A 通過(guò)接口 I 間接與類(lèi) B 或者類(lèi) C 發(fā)生聯(lián)系钱贯,則會(huì)大大降低修改類(lèi) A 的幾率挫掏。要做到可擴(kuò)展高復(fù)用,盡量不要讓業(yè)務(wù)邏輯層依賴(lài)數(shù)據(jù)層秩命,可以在數(shù)據(jù)層抽象出一個(gè)接口尉共,讓業(yè)務(wù)邏輯層依賴(lài)于這個(gè)抽象接口。

比如:母親給孩子講故事弃锐,只要給她一本書(shū)袄友,她就可以照著書(shū)給孩子講故事了。

//================== Book.h ==================

@interface Book : NSObject

/**
 故事內(nèi)容
 */
- (void)theStoryContent;

@end

//================== Mother.h ==================

@class Book;
@interface Mother : NSObject

/**
 講故事
 */
- (void)tellStory:(Book *)book;

@end

//================== main 函數(shù) ==================

Mother *mother = [Mother new];
Book *book = [Book new];
[mother tellStory:book];

運(yùn)行結(jié)果如下霹菊,

2018-11-09 14:52:08.759154+0800 DesignPatterns[6135:458778] 媽媽開(kāi)始講故事
2018-11-09 14:52:08.759365+0800 DesignPatterns[6135:458778] 很久很久以前有一個(gè)阿拉伯的故事……

將來(lái)有一天剧蚣,需求變更成,增加讓母親講一下報(bào)紙上的故事的功能旋廷,如下:

//================== Newspaper.h ==================

@interface Newspaper : NSObject

/**
 報(bào)紙內(nèi)容
 */
- (void)theStoryContent;

@end

如果將 Newspaper 類(lèi)替換 Book 類(lèi)鸠按,發(fā)現(xiàn)母親看不懂報(bào)紙上的故事,必須要修改 Mother 類(lèi)里面的 tellStory 方法才能看不懂報(bào)紙上的故事饶碘。假如以后需求換成雜志呢目尖?換成網(wǎng)頁(yè)呢?還要不斷地修改Mother 類(lèi)熊镣,這顯然不是好的設(shè)計(jì)卑雁,高層模塊都依賴(lài)了低層模塊的改動(dòng)募书,因此上述設(shè)計(jì)不符合依賴(lài)倒置原則绪囱。Mother 類(lèi)與 Book 類(lèi)之間的耦合性太高了,必須降低他們之間的耦合度才行莹捡。

解決方案鬼吵,將母親講故事的方法抽象一個(gè)接口或者 Protocol,讓Mother 類(lèi)不再依賴(lài) NewspaperBook 類(lèi)具體實(shí)現(xiàn)篮赢,而是依賴(lài)抽象出來(lái)的接口或者 Protocol齿椅。并且 NewspaperBook 類(lèi)也都依賴(lài)這個(gè)抽象出來(lái)的接口或者 Protocol,通過(guò)實(shí)現(xiàn)接口或者 Protocol 來(lái)做自己的事情启泣。

//================== IReaderProtocol.h ==================

@protocol IReaderProtocol <NSObject>

/**
 故事內(nèi)容
 */
- (void)theStoryContent;

@end

Mother 類(lèi)與接口 IReader 發(fā)生依賴(lài)關(guān)系涣脚,而 BookNewspaper 都屬于讀物的范疇,他們各自都去實(shí)現(xiàn) IReader 接口寥茫,這樣就符合依賴(lài)倒置原則了遣蚀,代碼修改為:

//================== Book.h ==================

@interface Book : NSObject <IReaderProtocol>

@end

//================== Newspaper.h ==================

@interface Newspaper : NSObject <IReaderProtocol>

@end

//================== IReaderProtocol.h ==================

@protocol IReaderProtocol <NSObject>

/**
 故事內(nèi)容
 */
- (void)theStoryContent;

@end

//================== Mother.h ==================

@interface Mother : NSObject

/**
 講故事
 */
- (void)tellStory:(NSObject<IReaderProtocol> *)reading;

@end

@implementation Mother

- (void)tellStory:(NSObject<IReaderProtocol> *)reading {
    NSLog(@"媽媽開(kāi)始講故事");
    if ([reading respondsToSelector:@selector(theStoryContent)]) {
        [reading theStoryContent];
    }
}

@end

//================== main 函數(shù) ==================

Mother *mother = [Mother new];
Book *book = [Book new];
Newspaper *newspaper = [Newspaper new];
[mother tellStory:book];
[mother tellStory:newspaper];

運(yùn)行結(jié)果如下,

2018-11-09 15:28:01.182603+0800 DesignPatterns[7055:532924] 媽媽開(kāi)始講故事
2018-11-09 15:28:01.182879+0800 DesignPatterns[7055:532924] 很久很久以前有一個(gè)阿拉伯的故事……
2018-11-09 15:28:01.182916+0800 DesignPatterns[7055:532924] 媽媽開(kāi)始講故事
2018-11-09 15:28:01.182955+0800 DesignPatterns[7055:532924] 雄鹿終結(jié)勇士八連勝……

這樣修改后,無(wú)論以后怎樣擴(kuò)展 main 函數(shù)芭梯,都不需要再修改 Mother 類(lèi)了险耀。這里只是舉了一個(gè)比較簡(jiǎn)單的例子,在實(shí)際的項(xiàng)目開(kāi)發(fā)中玖喘,盡可能的采用“低耦合甩牺,高內(nèi)聚”的原則,采用依賴(lài)倒置原則給多人并行開(kāi)發(fā)帶來(lái)了極大的便利累奈,無(wú)論是面向過(guò)程編程還是面向?qū)ο缶幊瘫崤桑挥惺垢鱾€(gè)模塊之間的耦合盡量的低,才能提高代碼的復(fù)用率澎媒。所以遵循依賴(lài)倒置原則可以降低類(lèi)之間的耦合性赠群,提高系統(tǒng)的穩(wěn)定性,降低修改程序造成的風(fēng)險(xiǎn)旱幼。

原則四查描、接口隔離原則(Interface Segregation Principle,簡(jiǎn)稱(chēng)ISP)

定義:客戶(hù)端不應(yīng)該依賴(lài)它不需要的接口;一個(gè)類(lèi)對(duì)另一個(gè)類(lèi)的依賴(lài)應(yīng)該建立在最小的接口上柏卤。

Class 'ClassB' does not conform to protocol 'InterfaceH'
Class 'ClassD' does not conform to protocol 'InterfaceH'

注意:在 Objective-C 中的協(xié)議可以通過(guò) @optional 關(guān)鍵字聲明不需要必須實(shí)現(xiàn)的方法冬三,這個(gè)只是 Objective-C 的一個(gè)特性,可以消除在 ClassBClassD 中沒(méi)有實(shí)現(xiàn) InterfaceHprotocol 協(xié)議缘缚。

比如勾笆,類(lèi) A 依賴(lài)接口 H 中的方法1、方法2桥滨、方法5窝爪,類(lèi) B 是對(duì)類(lèi) A 依賴(lài)的實(shí)現(xiàn)。類(lèi) C 依賴(lài)接口 H 中的方法3齐媒、方法4蒲每、方法5,類(lèi) D 是對(duì)類(lèi) C 依賴(lài)的實(shí)現(xiàn)喻括。對(duì)于類(lèi) B 和類(lèi) D 來(lái)說(shuō)邀杏,雖然他們都存在著用不到的方法,但由于實(shí)現(xiàn)了接口 H唬血,因?yàn)榻涌?H 對(duì)于類(lèi) A 和類(lèi) C 來(lái)說(shuō)不是最小接口望蜡,所以也必須要實(shí)現(xiàn)這些用不到的方法。

//================== InterfaceH.h ==================

@protocol InterfaceH <NSObject>

- (void)method1;
- (void)method2;
- (void)method3;
- (void)method4;
- (void)method5;

@end

//================== ClassB.h ==================

@interface ClassB : NSObject <InterfaceH>

@end

@implementation ClassB

- (void)method1 {
    NSLog(@"類(lèi) B 實(shí)現(xiàn)接口 H 的方法1");
}

- (void)method2 {
    NSLog(@"類(lèi) B 實(shí)現(xiàn)接口 H 的方法2");
}

- (void)method3 {
    //not necessarily
}

- (void)method4 {
    //not necessarily
}

- (void)method5 {
    NSLog(@"類(lèi) B 實(shí)現(xiàn)接口 H 的方法5");
}

@end

//================== ClassA.h ==================

@interface ClassA : NSObject

- (void)depend:(NSObject<InterfaceH> *)classB;

@end

@implementation ClassA

- (void)depend:(NSObject<InterfaceH> *)classB {
    
    if ([classB respondsToSelector:@selector(method1)]) {
        [classB method1];
    }
    if ([classB respondsToSelector:@selector(method2)]) {
        [classB method2];
    }
    if ([classB respondsToSelector:@selector(method5)]) {
        [classB method5];
    }
}

@end

//================== ClassD.h ==================

@interface ClassD : NSObject <InterfaceH>

@end

@implementation ClassD

- (void)method1 { 
    //not necessarily
}

- (void)method2 { 
    //not necessarily
}

- (void)method3 { 
    NSLog(@"類(lèi) D 實(shí)現(xiàn)接口 H 的方法3");
}

- (void)method4 { 
    NSLog(@"類(lèi) D 實(shí)現(xiàn)接口 H 的方法4");
}

- (void)method5 { 
    NSLog(@"類(lèi) D 實(shí)現(xiàn)接口 H 的方法5");
}

@end

//================== ClassC.h ==================

@interface ClassC : NSObject

- (void)depend:(NSObject<InterfaceH> *)classD;

@end

@implementation ClassC

- (void)depend:(NSObject<InterfaceH> *)classD {
    
    if ([classD respondsToSelector:@selector(method3)]) {
        [classD method3];
    }
    if ([classD respondsToSelector:@selector(method4)]) {
        [classD method4];
    }
    if ([classD respondsToSelector:@selector(method5)]) {
        [classD method5];
    }
}

@end

可以看到拷恨,如果接口過(guò)于臃腫脖律,只要接口中出現(xiàn)的方法,不管對(duì)依賴(lài)于它的類(lèi)有沒(méi)有用處腕侄,實(shí)現(xiàn)類(lèi)中都必須去實(shí)現(xiàn)這些方法小泉,這顯然不是好的設(shè)計(jì)勒叠。由于接口方法的設(shè)計(jì)造成了冗余,因此該設(shè)計(jì)不符合接口隔離原則膏孟。

解決方法:將臃腫的接口 H 拆分為獨(dú)立的幾個(gè)接口眯分,類(lèi) A 和類(lèi) C 分別與他們需要的接口建立依賴(lài)關(guān)系,也就是采用接口隔離原則柒桑。

//================== InterfaceH.h ==================

@protocol InterfaceH <NSObject>

- (void)method5;

@end

@protocol InterfaceH1 <InterfaceH>

- (void)method1;
- (void)method2;

@end

@protocol InterfaceH2 <InterfaceH>

- (void)method3;
- (void)method4;

@end

//================== ClassB.h ==================

@interface ClassB : NSObject <InterfaceH1>

@end

@implementation ClassB

- (void)method1 {
    NSLog(@"類(lèi) B 實(shí)現(xiàn)接口 H 的方法1");
}

- (void)method2 {
    NSLog(@"類(lèi) B 實(shí)現(xiàn)接口 H 的方法2");
}

- (void)method5 {
    NSLog(@"類(lèi) B 實(shí)現(xiàn)接口 H 的方法5");
}

@end

//================== ClassA.h ==================

@interface ClassA : NSObject

- (void)depend:(NSObject<InterfaceH1> *)classB;

@end

@implementation ClassA

- (void)depend:(NSObject<InterfaceH1> *)classB {
    
    if ([classB respondsToSelector:@selector(method1)]) {
        [classB method1];
    }
    if ([classB respondsToSelector:@selector(method2)]) {
        [classB method2];
    }
    if ([classB respondsToSelector:@selector(method5)]) {
        [classB method5];
    }
}

@end

//================== ClassD.h ==================

@interface ClassD : NSObject <InterfaceH2>

@end

@implementation ClassD

- (void)method3 { 
    NSLog(@"類(lèi) D 實(shí)現(xiàn)接口 H 的方法3");
}

- (void)method4 { 
    NSLog(@"類(lèi) D 實(shí)現(xiàn)接口 H 的方法4");
}

- (void)method5 { 
    NSLog(@"類(lèi) D 實(shí)現(xiàn)接口 H 的方法5");
}

@end

//================== ClassC.h ==================

@interface ClassC : NSObject

- (void)depend:(NSObject<InterfaceH2> *)classD;

@end

@implementation ClassC

- (void)depend:(NSObject<InterfaceH2> *)classD {
    
    if ([classD respondsToSelector:@selector(method3)]) {
        [classD method3];
    }
    if ([classD respondsToSelector:@selector(method4)]) {
        [classD method4];
    }
    if ([classD respondsToSelector:@selector(method5)]) {
        [classD method5];
    }
}

@end

接口隔離原則的含義是:建立單一接口弊决,不要建立龐大臃腫的接口,盡量細(xì)化接口魁淳,接口中的方法盡量少飘诗。在實(shí)際項(xiàng)目開(kāi)發(fā)中,只暴露給調(diào)用的類(lèi)需要的方法界逛,不需要的方法則隱藏起來(lái)昆稿。只有專(zhuān)注地為一個(gè)模塊提供定制服務(wù),才能建立最小的依賴(lài)關(guān)系息拜,不要試圖去建立一個(gè)很龐大的接口供所有依賴(lài)它的類(lèi)去調(diào)用溉潭。通過(guò)分散定義多個(gè)接口,可以預(yù)防外來(lái)變更的擴(kuò)散少欺,提高系統(tǒng)的靈活性和可維護(hù)性喳瓣。

原則五、迪米特法則(Law of Demeter赞别,簡(jiǎn)稱(chēng)LOD)

定義:一個(gè)對(duì)象應(yīng)該對(duì)其他對(duì)象保持最少的了解畏陕。

當(dāng)類(lèi)與類(lèi)之間的關(guān)系越密切,耦合度越大仿滔,當(dāng)一個(gè)類(lèi)發(fā)生改變時(shí)惠毁,對(duì)另一個(gè)類(lèi)的影響也越大。通俗的來(lái)講崎页,就是一個(gè)類(lèi)對(duì)自己依賴(lài)的類(lèi)知道的越少越好鞠绰。也就是說(shuō),對(duì)于被依賴(lài)的類(lèi)來(lái)說(shuō)实昨,無(wú)論邏輯多么復(fù)雜洞豁,都盡量地的將邏輯封裝在類(lèi)的內(nèi)部,對(duì)外只暴露必要的接口荒给。

解決方案:盡量降低類(lèi)與類(lèi)之間的耦合。

比如刁卜,有一個(gè)集團(tuán)公司志电,下屬單位有分公司和直屬部門(mén),現(xiàn)在要求打印出所有下屬單位的員工 ID
Model 類(lèi)蛔趴,

//================== EmployeeModel.h ==================

@interface EmployeeModel : NSObject

/**
 總公司員工ID
 */
@property (nonatomic, copy) NSString *employee_id;

@end

//================== SubEmployeeModel.h ==================

@interface SubEmployeeModel : NSObject

/**
 分公司員工ID
 */
@property (nonatomic, copy) NSString *subemployee_id;

@end

Company 類(lèi)挑辆,

//================== Company.h ==================

@interface Company : NSObject

- (NSArray *)getAllEmployee;

- (void)printAllEmployeeWithSubCompany:(SubCompany *)subCompany;

@end

@implementation Company

- (NSArray *)getAllEmployee {
    NSMutableArray<EmployeeModel *> *employeeArray = [NSMutableArray<EmployeeModel *> array];
    for (int i = 0; i < 3; i++) {
        EmployeeModel *employeeModel = [[EmployeeModel alloc] init];
        [employeeModel setEmployee_id:[@(i) stringValue]];
        [employeeArray addObject:employeeModel];
    }
    return employeeArray.copy;
}

- (void)printAllEmployeeWithSubCompany:(SubCompany *)subCompany {
    // 分公司員工
    NSArray<SubEmployeeModel *> *subEmployeeArray = subCompany.getAllEmployee;
    for (SubEmployeeModel *employeeModel in subEmployeeArray) {
        NSLog(@"分公司員工ID:%@", employeeModel.subemployee_id);
    }
    
    // 總公司員工
    NSArray<EmployeeModel *> *employeeArray = self.getAllEmployee;
    for (EmployeeModel *employeeModel in employeeArray) {
        NSLog(@"總公司員工ID:%@", employeeModel.employee_id);
    }
}

@end

//================== SubCompany.h ==================

@interface SubCompany : NSObject

- (NSArray *)getAllEmployee;

@end

@implementation SubCompany

- (NSArray *)getAllEmployee {
    NSMutableArray<SubEmployeeModel *> *employeeArray = [NSMutableArray<SubEmployeeModel *> array];
    for (int i = 0; i < 3; i++) {
        SubEmployeeModel *employeeModel = [[SubEmployeeModel alloc] init];
        [employeeModel setSubemployee_id:[@(i) stringValue]];
        [employeeArray addObject:employeeModel];
    }
    return employeeArray.copy;
}

@end

從上面可以看出,打印 Company 所有員工的 ID,需要依賴(lài)分公司 SubCompany鱼蝉。但是在 printAllEmployeeWithSubCompany: 方法里面必須要初始化分公司員工 SubEmployeeModel洒嗤。而SubEmployeeModelCompany 并不是直接聯(lián)系,換句話(huà)說(shuō)魁亦,總公司 Company 只需要依賴(lài)分公司 SubCompany渔隶,與分公司的員工 SubEmployeeModel 并沒(méi)有任何聯(lián)系,這樣設(shè)計(jì)顯然是增加了不必要的耦合洁奈。

按照迪米特法則间唉,類(lèi)與類(lèi)之間的應(yīng)該減少不必要的關(guān)聯(lián)程度。

//================== Company.h ==================

@interface Company : NSObject

/**
 獲取所有分公司員工
 */
- (NSArray *)getAllEmployee;

/**
 打印公司所有員工
 */
- (void)printAllEmployeeWithSubCompany:(SubCompany *)subCompany;

@end

@implementation Company

- (NSArray *)getAllEmployee {
    NSMutableArray<EmployeeModel *> *employeeArray = [NSMutableArray<EmployeeModel *> array];
    for (int i = 0; i < 3; i++) {
        EmployeeModel *employeeModel = [[EmployeeModel alloc] init];
        [employeeModel setEmployee_id:[@(i) stringValue]];
        [employeeArray addObject:employeeModel];
    }
    return employeeArray.copy;
}

- (void)printAllEmployeeWithSubCompany:(SubCompany *)subCompany {
    // 分公司員工
    [subCompany printAllEmployee];
    
    // 總公司員工
    NSArray<EmployeeModel *> *employeeArray = self.getAllEmployee;
    for (EmployeeModel *employeeModel in employeeArray) {
        NSLog(@"總公司員工ID:%@", employeeModel.employee_id);
    }
}

@end

//================== SubCompany.h ==================

@interface SubCompany : NSObject

/**
 獲取所有分公司員工
 */
- (NSArray *)getAllEmployee;

/**
 打印分公司所有員工
 */
- (void)printAllEmployee;

@end

@implementation SubCompany

- (NSArray *)getAllEmployee {
    NSMutableArray<SubEmployeeModel *> *employeeArray = [NSMutableArray<SubEmployeeModel *> array];
    for (int i = 0; i < 3; i++) {
        SubEmployeeModel *employeeModel = [[SubEmployeeModel alloc] init];
        [employeeModel setSubemployee_id:[@(i) stringValue]];
        [employeeArray addObject:employeeModel];
    }
    return employeeArray.copy;
}

- (void)printAllEmployee {
    // 分公司員工
    NSArray<SubEmployeeModel *> *subEmployeeArray = self.getAllEmployee;
    for (SubEmployeeModel *employeeModel in subEmployeeArray) {
        NSLog(@"分公司員工ID:%@", employeeModel.subemployee_id);
    }
}

@end

修改后利术,為分公司增加了打印所有公鑰 ID 的方法呈野,總公司直接調(diào)分公司的打印方法,從而避免了與分公司的員工發(fā)生耦合印叁。

耦合的方式很多被冒,依賴(lài)、關(guān)聯(lián)轮蜕、組合姆打、聚合等。

迪米特法則的初衷是降低類(lèi)之間的耦合肠虽,由于每個(gè)類(lèi)都減少了不必要的依賴(lài)幔戏,因此的確可以降低耦合關(guān)系。但是過(guò)分的使用迪米特原則税课,會(huì)產(chǎn)生大量傳遞類(lèi)闲延,導(dǎo)致系統(tǒng)復(fù)雜度變大。所以在采用迪米特法則時(shí)要反復(fù)權(quán)衡韩玩,既做到結(jié)構(gòu)清晰垒玲,又要高內(nèi)聚低耦合。

原則六找颓、開(kāi)閉原則(Open Close Principle合愈,簡(jiǎn)稱(chēng)OCP)

定義:一個(gè)軟件實(shí)體如類(lèi)、模塊和函數(shù)應(yīng)該對(duì)擴(kuò)展開(kāi)放击狮,對(duì)修改關(guān)閉佛析。

核心思想:盡量通過(guò)擴(kuò)展應(yīng)用程序中的類(lèi)、模塊和函數(shù)來(lái)解決不同的需求場(chǎng)景彪蓬,而不是通過(guò)直接修改已有的類(lèi)寸莫、模塊和函數(shù)。

用抽象構(gòu)建框架档冬,用實(shí)現(xiàn)擴(kuò)展細(xì)節(jié)膘茎,對(duì)擴(kuò)展開(kāi)放的關(guān)鍵是抽象桃纯,而對(duì)象的多態(tài)則保證了這種擴(kuò)展的開(kāi)放性。開(kāi)放原則首先意味著我們可以自由地增加功能披坏,而不會(huì)影響原有功能态坦。這就要求我們能夠通過(guò)繼承完成功能的擴(kuò)展。其次棒拂,開(kāi)放原則還意味著實(shí)現(xiàn)是可替換的伞梯。只有利用抽象,才可以為定義提供不同的實(shí)現(xiàn)着茸,然后根據(jù)不同的需求實(shí)例化不同的實(shí)現(xiàn)子類(lèi)壮锻。

開(kāi)放封閉原則的優(yōu)點(diǎn):

  • 代碼可讀性高,可維護(hù)性強(qiáng)涮阔。
  • 幫助縮小邏輯粒度猜绣,以提高可復(fù)用性。
  • 可以使維護(hù)人員只擴(kuò)展一個(gè)類(lèi)掰邢,而非修改一個(gè)類(lèi),從而提高可維護(hù)性伟阔。
  • 在設(shè)計(jì)之初考慮所有可能變化的因素辣之,留下接口,從而符合面向?qū)ο箝_(kāi)發(fā)的要求皱炉。

比如怀估,書(shū)店售書(shū)的經(jīng)典例子:

//================== IBookProtocol.h ==================

@protocol IBookProtocol <NSObject>

/**
 獲取書(shū)籍名稱(chēng)
 */
- (NSString *)bookName;

/**
 獲取書(shū)籍售價(jià)
 */
- (CGFloat)bookPrice;

/**
 獲取書(shū)籍作者
 */
- (NSString *)bookAuthor;

@end

//================== NovelBook.h ==================

@interface NovelBook : NSObject <IBookProtocol>

- (instancetype)initWithBookName:(NSString *)name
                           price:(CGFloat)price
                          author:(NSString *)author;

@end

//================== BookStore.h ==================

@interface BookStore : NSObject

- (NSArray<IBookProtocol> *)bookArray;

@end

//================== main 函數(shù) ==================

// 模擬書(shū)店賣(mài)書(shū)
BookStore *bookStore = [BookStore new];
for (NovelBook *novelBook in bookStore.bookArray) {
    NSLog(@"書(shū)籍名稱(chēng):%@ 書(shū)籍作者:%@ 書(shū)籍價(jià)格:%2f", [novelBook bookName], [novelBook bookAuthor], [novelBook bookPrice]);
}

運(yùn)行結(jié)果如下,

2018-11-12 15:11:32.642070+0800 DesignPatterns[1863:5763476] 書(shū)籍名稱(chēng):天龍八部 書(shū)籍作者:金庸 書(shū)籍價(jià)格:50.000000
2018-11-12 15:11:32.642495+0800 DesignPatterns[1863:5763476] 書(shū)籍名稱(chēng):巴黎圣母院 書(shū)籍作者:雨果 書(shū)籍價(jià)格:70.000000
2018-11-12 15:11:32.642530+0800 DesignPatterns[1863:5763476] 書(shū)籍名稱(chēng):悲慘世界 書(shū)籍作者:雨果 書(shū)籍價(jià)格:80.000000
2018-11-12 15:11:32.642558+0800 DesignPatterns[1863:5763476] 書(shū)籍名稱(chēng):金瓶梅 書(shū)籍作者:蘭陵王 書(shū)籍價(jià)格:40.000000

將來(lái)某一天需求變更為項(xiàng)目投產(chǎn)合搅,書(shū)店盈利多搀,書(shū)店決定灾部,40 元以上打 8 折康铭,40 元以下打 9 折。

在實(shí)際的項(xiàng)目開(kāi)發(fā)中赌髓,如果不懂得開(kāi)閉原則的話(huà)从藤,很容易犯下面的錯(cuò)誤:

  • IBookProtocol 上新增加一個(gè)方法 bookOffPrice() 方法,專(zhuān)門(mén)進(jìn)行打折锁蠕,所有實(shí)現(xiàn)類(lèi)實(shí)現(xiàn)這個(gè)方法夷野,但是如果其他不想打折的書(shū)籍也會(huì)因?yàn)閷?shí)現(xiàn)了書(shū)籍的接口必須打折。
  • 修改 NovelBook 實(shí)現(xiàn)類(lèi)中的 bookPrice() 方中實(shí)現(xiàn)打折處理匿沛,由于該方法已經(jīng)實(shí)現(xiàn)了打折處理價(jià)格扫责,因此采購(gòu)書(shū)籍人員看到的也是打折后的價(jià)格的情況。

很顯然按照上面兩種方案的話(huà)逃呼,隨著需求的增加鳖孤,需要反復(fù)修改之前創(chuàng)建的類(lèi),給新增的類(lèi)造成了不必要的冗余抡笼,業(yè)務(wù)邏輯的處理和需求不相符合等情況苏揣。

//================== OffNovelBook.h ==================

@interface OffNovelBook : NovelBook

@end

@implementation OffNovelBook

- (instancetype)initWithBookName:(NSString *)name
                           price:(CGFloat)price
                          author:(NSString *)author {
    return [super initWithBookName:name price:price author:author];
}

- (CGFloat)bookPrice {
    CGFloat originalPrice = [super bookPrice];
    CGFloat offPrice      = 0;
    if (originalPrice > 40) {
        offPrice = originalPrice * 0.8;
    } else {
        offPrice = originalPrice * 0.9;
    }
    return offPrice;
}

@end

//================== BookStore.h ==================

@interface BookStore : NSObject

- (NSArray<IBookProtocol> *)bookArray;

- (NSArray<IBookProtocol> *)offBookArray;

@end

@implementation BookStore

- (NSArray<IBookProtocol> *)bookArray {
    NSMutableArray<IBookProtocol> *tempArray = [NSMutableArray<IBookProtocol> array];
    
    NovelBook *book1 = [[NovelBook alloc] initWithBookName:@"天龍八部" price:30 author:@"金庸"];
    [tempArray addObject:book1];
    
    NovelBook *book2 = [[NovelBook alloc] initWithBookName:@"巴黎圣母院" price:70 author:@"雨果"];
    [tempArray addObject:book2];
    
    NovelBook *book3 = [[NovelBook alloc] initWithBookName:@"悲慘世界" price:80 author:@"雨果"];
    [tempArray addObject:book3];
    
    NovelBook *book4 = [[NovelBook alloc] initWithBookName:@"金瓶梅" price:40 author:@"蘭陵王"];
    [tempArray addObject:book4];
    return tempArray;
}

- (NSArray<IBookProtocol> *)offBookArray {
    NSMutableArray<IBookProtocol> *tempArray = [NSMutableArray<IBookProtocol> array];
    
    OffNovelBook *book1 = [[OffNovelBook alloc] initWithBookName:@"天龍八部" price:30 author:@"金庸"];
    [tempArray addObject:book1];
    
    OffNovelBook *book2 = [[OffNovelBook alloc] initWithBookName:@"巴黎圣母院" price:70 author:@"雨果"];
    [tempArray addObject:book2];
    
    OffNovelBook *book3 = [[OffNovelBook alloc] initWithBookName:@"悲慘世界" price:80 author:@"雨果"];
    [tempArray addObject:book3];
    
    OffNovelBook *book4 = [[OffNovelBook alloc] initWithBookName:@"金瓶梅" price:40 author:@"蘭陵王"];
    [tempArray addObject:book4];
    return tempArray;
}

@end

//================== main 函數(shù) ==================

BookStore *bookStore = [BookStore new];

NSLog(@"------------書(shū)店賣(mài)出去的原價(jià)書(shū)籍記錄如下:------------");
for (NovelBook *novelBook in bookStore.bookArray) {
    NSLog(@"書(shū)籍名稱(chēng):%@ 書(shū)籍作者:%@ 書(shū)籍價(jià)格:%2f", [novelBook bookName], [novelBook bookAuthor], [novelBook bookPrice]);
}

NSLog(@"------------書(shū)店賣(mài)出去的打折書(shū)籍記錄如下:------------");
for (OffNovelBook *novelBook in bookStore.offBookArray) {
    NSLog(@"書(shū)籍名稱(chēng):%@ 書(shū)籍作者:%@ 書(shū)籍價(jià)格:%2f", [novelBook bookName], [novelBook bookAuthor], [novelBook bookPrice]);
}

運(yùn)行結(jié)果如下,

2018-11-12 15:52:01.639550+0800 DesignPatterns[2962:6151804] ------------書(shū)店賣(mài)出去的原價(jià)書(shū)籍記錄如下:------------
2018-11-12 15:52:01.639895+0800 DesignPatterns[2962:6151804] 書(shū)籍名稱(chēng):天龍八部 書(shū)籍作者:金庸 書(shū)籍價(jià)格:30.000000
2018-11-12 15:52:01.639927+0800 DesignPatterns[2962:6151804] 書(shū)籍名稱(chēng):巴黎圣母院 書(shū)籍作者:雨果 書(shū)籍價(jià)格:70.000000
2018-11-12 15:52:01.639951+0800 DesignPatterns[2962:6151804] 書(shū)籍名稱(chēng):悲慘世界 書(shū)籍作者:雨果 書(shū)籍價(jià)格:80.000000
2018-11-12 15:52:01.639971+0800 DesignPatterns[2962:6151804] 書(shū)籍名稱(chēng):金瓶梅 書(shū)籍作者:蘭陵王 書(shū)籍價(jià)格:40.000000
2018-11-12 15:52:01.639988+0800 DesignPatterns[2962:6151804] ------------書(shū)店賣(mài)出去的打折書(shū)籍記錄如下:------------
2018-11-12 15:52:01.640029+0800 DesignPatterns[2962:6151804] 書(shū)籍名稱(chēng):天龍八部 書(shū)籍作者:金庸 書(shū)籍價(jià)格:27.000000
2018-11-12 15:52:01.640145+0800 DesignPatterns[2962:6151804] 書(shū)籍名稱(chēng):巴黎圣母院 書(shū)籍作者:雨果 書(shū)籍價(jià)格:56.000000
2018-11-12 15:52:01.640194+0800 DesignPatterns[2962:6151804] 書(shū)籍名稱(chēng):悲慘世界 書(shū)籍作者:雨果 書(shū)籍價(jià)格:64.000000
2018-11-12 15:52:01.640217+0800 DesignPatterns[2962:6151804] 書(shū)籍名稱(chēng):金瓶梅 書(shū)籍作者:蘭陵王 書(shū)籍價(jià)格:36.000000

在實(shí)際的項(xiàng)目開(kāi)發(fā)中推姻,

  • 對(duì)抽象定義的修改平匈,要保證定義的接口或者 Protocol 的穩(wěn)定轻抱,尤其要保證被其他對(duì)象調(diào)用的接口的穩(wěn)定蝇恶;否則俏讹,就會(huì)導(dǎo)致修改蔓延赤套,牽一發(fā)而動(dòng)全身官卡。

  • 對(duì)具體實(shí)現(xiàn)的修改枚粘,因?yàn)榫唧w實(shí)現(xiàn)的修改户敬,可能會(huì)給調(diào)用者帶來(lái)意想不到的結(jié)果唉擂。如果確實(shí)需要修改具體的實(shí)現(xiàn)厂捞,就需要做好達(dá)到測(cè)試覆蓋率要求的單元測(cè)試输玷。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市靡馁,隨后出現(xiàn)的幾起案子欲鹏,更是在濱河造成了極大的恐慌,老刑警劉巖臭墨,帶你破解...
    沈念sama閱讀 221,820評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件赔嚎,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡胧弛,警方通過(guò)查閱死者的電腦和手機(jī)尤误,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,648評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)叶圃,“玉大人袄膏,你說(shuō)我怎么就攤上這事〔艄冢” “怎么了沉馆?”我有些...
    開(kāi)封第一講書(shū)人閱讀 168,324評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)德崭。 經(jīng)常有香客問(wèn)我斥黑,道長(zhǎng),這世上最難降的妖魔是什么眉厨? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,714評(píng)論 1 297
  • 正文 為了忘掉前任锌奴,我火速辦了婚禮,結(jié)果婚禮上憾股,老公的妹妹穿的比我還像新娘鹿蜀。我一直安慰自己箕慧,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,724評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布茴恰。 她就那樣靜靜地躺著颠焦,像睡著了一般。 火紅的嫁衣襯著肌膚如雪往枣。 梳的紋絲不亂的頭發(fā)上伐庭,一...
    開(kāi)封第一講書(shū)人閱讀 52,328評(píng)論 1 310
  • 那天,我揣著相機(jī)與錄音分冈,去河邊找鬼圾另。 笑死,一個(gè)胖子當(dāng)著我的面吹牛雕沉,可吹牛的內(nèi)容都是我干的集乔。 我是一名探鬼主播,決...
    沈念sama閱讀 40,897評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼蘑秽,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼饺著!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起肠牲,我...
    開(kāi)封第一講書(shū)人閱讀 39,804評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤幼衰,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后缀雳,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體渡嚣,經(jīng)...
    沈念sama閱讀 46,345評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,431評(píng)論 3 340
  • 正文 我和宋清朗相戀三年肥印,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了识椰。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,561評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡深碱,死狀恐怖腹鹉,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情敷硅,我是刑警寧澤功咒,帶...
    沈念sama閱讀 36,238評(píng)論 5 350
  • 正文 年R本政府宣布,位于F島的核電站绞蹦,受9級(jí)特大地震影響力奋,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜幽七,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,928評(píng)論 3 334
  • 文/蒙蒙 一景殷、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦猿挚、人聲如沸咐旧。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,417評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)休偶。三九已至梁厉,卻和暖如春辜羊,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背词顾。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,528評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工八秃, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人肉盹。 一個(gè)月前我還...
    沈念sama閱讀 48,983評(píng)論 3 376
  • 正文 我出身青樓昔驱,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親上忍。 傳聞我的和親對(duì)象是個(gè)殘疾皇子骤肛,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,573評(píng)論 2 359

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

  • 前言 關(guān)于設(shè)計(jì)模式六大設(shè)計(jì)原則的資料網(wǎng)上很多,但感覺(jué)很多地方解釋地都太過(guò)于籠統(tǒng)化窍蓝,特此再總結(jié)一波腋颠。 優(yōu)化第一步-單...
    ghroost閱讀 1,110評(píng)論 0 5
  • 單一職責(zé)原則 (SRP) 全稱(chēng) SRP , Single Responsibility Principle 單一職...
    米莉_L閱讀 1,768評(píng)論 2 5
  • 本文出自《Android源碼設(shè)計(jì)模式解析與實(shí)戰(zhàn)》中的第一章。 1吓笙、優(yōu)化代碼的第一步——單一職責(zé)原則 單一職責(zé)原則的...
    MrSimp1e0閱讀 1,776評(píng)論 1 13
  • 設(shè)計(jì)模式概述 在學(xué)習(xí)面向?qū)ο笃叽笤O(shè)計(jì)原則時(shí)需要注意以下幾點(diǎn):a) 高內(nèi)聚淑玫、低耦合和單一職能的“沖突”實(shí)際上,這兩者...
    彥幀閱讀 3,752評(píng)論 0 14
  • 畫(huà)樓紅濕杜鵑開(kāi)面睛, 林表游芳逸興催絮蒿。 對(duì)酒長(zhǎng)歌尋曲水, 吟風(fēng)弄柳得詩(shī)回叁鉴。 一聲梧葉飛來(lái)夢(mèng)土涝, 數(shù)點(diǎn)叢花染玉臺(tái)。 寄語(yǔ)浮...
    郭大牛閱讀 1,611評(píng)論 11 44