定義
There should never be more than one reason for a class to change.
應(yīng)該有且僅有一個原因引起類的變更逢唤。
優(yōu)點
降低類的復(fù)雜性
每個類實現(xiàn)單一職責(zé),并且單一職責(zé)都有清楚明確的定義卵牍,復(fù)雜性當(dāng)然降低。
提高可讀性?
類的復(fù)雜性降低了厕倍,當(dāng)然提高了可讀性了兴蒸。
提高可維護(hù)性?
類的復(fù)雜性降低盾饮,可讀性好,當(dāng)然好維護(hù)既绩。
易于測試
測試單一目標(biāo)的類只需要很少的測試類概龄。讓“用測試替代文檔” “self documentation by tests”變得更加容易
易于調(diào)試
在一個單一職責(zé)類找到問題是一件更容易的事情。
變更引起的風(fēng)險降低饲握,變更是必不可少的私杜,如果接口的單一職責(zé)做的好蚕键,一個接口修改只對相應(yīng)的實現(xiàn)類有影響,對其它的接口沒有影響衰粹,這對系統(tǒng)的擴展性锣光,維護(hù)性都是有好處的。
類的單一職責(zé)原則
一般一個對象可以分為屬性和行為二部分铝耻,所以在類的設(shè)計時誊爹,我們一般把對象的屬性抽象成一個BO(Business Object,業(yè)務(wù)對象),把對象的行為抽象成一個Biz(Business Logic瓢捉,業(yè)務(wù)邏輯)频丘。
產(chǎn)生原因
沒有任何的程序設(shè)計人員不清楚應(yīng)該寫出高內(nèi)聚低耦合的程序,但是很多耦合常常發(fā)生在不經(jīng)意之間泡态,其原因就是:職責(zé)擴散:因為某種原因搂漠,某一職責(zé)被分化為顆粒度更細(xì)的多個職責(zé)了
解決辦法
遵守單一職責(zé)原則,將不同的職責(zé)封裝到不同的類或模塊中某弦。
場景模擬
我們需要創(chuàng)作一個論壇桐汤,需要發(fā)布主題和回帖功能
場景模擬UML 圖
簡單代碼
@protocol Thread<NSObject>
-(void)addReplayMessage;
@end
#import "Thread.h"
@protocol Forum<NSObject>
-(void)addThread:(id<Thread>)thread;
-(void)addReplayMessage;
@end
#import "Forum.h"
@interface ForumObject : NSObject<Forum>
@end
#import "ForumObject.h"
@interface ForumObject()
@property (nonatomic,strong) id<Thread>thread;
@end@implementation ForumObject
-(void)addThread:(id<Thread>)thread{
? ? self.thread = thread;
}
-(void)addReplayMessage{
? ? [self.thread addReplayMessage];
}
@end
#import "Thread.h"
@interface ThreadObject : NSObject<Thread>
@property (nonatomic,strong) NSString *name;
@end
#import "ThreadObject.h"
@implementation ThreadObject
-(void)addReplayMessage{
? ? NSLog(@"%@ 回復(fù)信息",self.name);
}
@end
測試代碼
id<Forum> forumObject = [ForumObject new];
ThreadObject * threadobject = [ThreadObject new];
threadobject.name =@"單一職責(zé)原則";
[forumObject addThread:threadobject];
[forumObject addReplayMessage];
測試結(jié)果
2018-04-04 14:39:55.425610+0800 設(shè)計模式原則[90446:6274362] 單一職責(zé)原則 回復(fù)信息
分析
我們都知道一個論壇的結(jié)構(gòu)一般是
forum------->Thread----------->Message
一個論壇Forum中有多個Thread ,一個Thread 有多個回帖和跟帖。
根據(jù)這個層次結(jié)構(gòu)靶壮,forum 增加回帖的功能有點太寬了怔毛,這個功能應(yīng)該屬于Thread的
代碼重構(gòu)
#import "Thread.h"
@protocol ForumNew<NSObject>
-(void)addThread:(id<NSObject>)thread;
@end
#import "ForumNew.h"
@interface ForumNewObject : NSObject<ForumNew>
@end
#import "ForumNewObject.h"
@interface ForumNewObject()
@property (nonatomic,strong) id<Thread>thread;
@end
@implementation ForumNewObject-
(void)addThread:(id<Thread>)thread{
? ? self.thread = thread;
}
@end
測試代碼
?id<ForumNew> forumObject= [ForumNewObject new];
? ?ThreadObject * threadobject = [ThreadObject new];
? ? threadobject.name =@"單一職責(zé)原則";
? ? [forumObject addThread:threadobject];
? ? [threadobject addReplayMessage];
結(jié)果
2018-04-04 14:50:13.955267+0800 設(shè)計模式原則[93059:6286368] 單一職責(zé)原則 回復(fù)信息
其實重構(gòu)代碼就是講回帖的功能從forum拿到了Thread中,代碼很簡單腾降,就是需要體會下拣度。
上述問題產(chǎn)生的原因是因為當(dāng)時我們把論壇的Thread和Message都當(dāng)做論壇的一個功能。由于后期業(yè)務(wù)變更螃壤,導(dǎo)致Thread ?和Message 需要劃分為更細(xì)致蜡娶。
如何識別SRP被破壞?
類有太多依賴
類的構(gòu)造器有太多參數(shù)映穗,意味著測試有太多依賴,需要制造mock太多測試輸入?yún)?shù)幕随,通常意味著已經(jīng)破壞SRP了蚁滋。
方法有太多參數(shù)
類似類的構(gòu)造器,方法參數(shù)意味著依賴赘淮。
測試類變得復(fù)雜
如果測試有太多變量辕录,意味著這個類有太多職責(zé)。
類或方法太長
如果方法太長梢卸,意味著內(nèi)容太多走诞,職責(zé)過多。
一個類不超過 200-250
描述性名稱
如果你需要描述你的類 方法或包蛤高,比如使用"xxx和xxx"這種語句蚣旱,意味著可能破壞了SRP.
低聚合Cohesion的類
聚合Cohesion是一個很重要的概念碑幅,雖然聚合是有關(guān)結(jié)構(gòu)概念,但是聚合和SRP非常相關(guān)塞绿,如前面論壇案例沟涨,如果一個類不代表一個高聚合,意味著低凝聚low Cohesion异吻,它就可能意味破壞SRP裹赴。一個低凝聚的特點:
一個類有兩個字段,其中一個字段被一些方法使用诀浪;另外一個字段被其他方法使用棋返。
在一個地方改動影響另外一個地方
如果在一個代碼地方加入新功能或只是簡單重構(gòu),卻影響了其他不相關(guān)的地方雷猪,意味著這個地方代碼可能破壞了SRP.
獵槍效果Shotgun Effect
如果一個小的改變引起一發(fā)動全身睛竣,這意味SRP被破壞了。
不能夠封裝模塊
比如使用Spring框架春宣,你使用@Configuration or XML 配置酵颁,如果你不能在一個配置中封裝一個Bean。意味著它有太多職責(zé)月帝,Spring配置應(yīng)該隱藏內(nèi)部bean躏惋,暴露最少接口,如果你因為多個原因需要改變Spring配置嚷辅,可能破壞了SRP.
借鑒博客
下一篇博客