目錄
接口隔離原則(ISP :Interface Segregation Principle)
定義
客戶端不應該依賴它不需要的接口。
一個類對另一個類的依賴應該建立在最小的接口上。
由來
類A通過接口I依賴類B,類C通過接口I依賴類D,如果接口I對于類A和類B來說不是最小接口,則類B和類D必須去實現他們不需要的方法。
Code
@protocol I <NSObject>
- (void)m1;
- (void)m2;
- (void)m3;
- (void)m4;
- (void)m5;
@end
@interface B : NSObject<I>
@end
@implementation B
- (void)m1{ }
- (void)m2{ }
- (void)m3{ }
//實現的多余方法
- (void)m4{ }
//實現的多余方法
- (void)m5{ }
@end
@interface A : NSObject
@end
@implementation A
- (void)m1:(id<I>)i{
[i m1];
}
- (void)m2:(id<I>)i{
[i m2];
}
- (void)m3:(id<I>)i{
[i m3];
}
@end
@interface D : NSObject<I>
@end
@implementation D
- (void)m1{ }
//實現的多余方法
- (void)m2{ }
//實現的多余方法
- (void)m3{ }
- (void)m4{ }
- (void)m5{ }
@end
@interface C : NSObject
@end
@implementation C
- (void)m1:(id<I>)i{
[i m1];
}
- (void)m4:(id<I>)i{
[i m4];
}
- (void)m5:(id<I>)i{
[i m5];
}
@end
解決方案
將臃腫的接口I拆分為獨立的幾個接口,類A和類C分別與他們需要的接口建立依賴關系。
Code
@protocol I <NSObject>
- (void)m1;
@end
@protocol I2 <NSObject>
- (void)m2;
- (void)m3;
@end
@protocol I3 <NSObject>
- (void)m4;
- (void)m5;
@end
@interface B : NSObject<I,I2>
@end
@implementation B
- (void)m1{ }
- (void)m2{ }
- (void)m3{ }
@end
@interface A : NSObject
@end
@implementation A
- (void)m1:(id<I>)i{
[i m1];
}
- (void)m2:(id<I2>)i{
[i m2];
}
- (void)m3:(id<I2>)i{
[i m3];
}
@end
@interface D : NSObject<I,I3>
@end
@implementation D
- (void)m1{ }
- (void)m4{ }
- (void)m5{ }
@end
@interface C : NSObject
@end
@implementation C
- (void)m1:(id<I>)i{
[i m1];
}
- (void)m4:(id<I3>)i{
[i m4];
}
- (void)m5:(id<I3>)i{
[i m5];
}
@end
優點
建立單一接口,不要建立龐大臃腫的接口,盡量細化接口,接口中的方法盡量少。也就是說,我們要為各個類建立專用的接口,而不要試圖去建立一個很龐大的接口供所有依賴它的類去調用。在程序設計中,依賴幾個專用的接口要比依賴一個綜合的接口更靈活。接口是設計時對外部設定的“契約”,通過分散定義多個接口,可以預防外來變更的擴散,提高系統的靈活性和可維護性。
思考
你是否覺得ISP跟之前的單一職責原則很相似?
其實不然。其一,單一職責原則原注重的是職責;而接口隔離原則注重對接口依賴的隔離。其二,單一職責原則主要是約束類,其次才是接口和方法,它針對的是程序中的實現和細節;而接口隔離原則主要約束接口接口,主要針對抽象,針對程序整體框架的構建。你是否覺得在iOS 中開發可以用協議的可選實現(@optional)來解決ISP問題?
其實不然,@optional關注的是可有可無,但這種可有可無不是完全沒有作用的。而ISP關注的是完全不需要實現的方法,對于實現類來說完全沒有作用的。
舉個例子來說明吧。
網絡請求協議
@protocol NetworkProtocol <NSObject>
- (void)loadSuccess;
- (void)loadFailed;
@optional
- (void)loadCancel;
@end
對于loadCancel方法,它跟網絡請求是相關的,但是它不希望每個實現者都必須處理它,所以使用@optional。
@protocol NetworkProtocol <NSObject>
- (void)loadSuccess;
- (void)loadFailed;
@optional
- (void)helloWorld;
@end
另外一種情況類似于helloWorld方法,這個方法跟網絡請求協議完全沒有相關,只是有個依賴需要他實現這個方法,這個時候用@optional就不是很合適,應該將helloWorld放到一個單獨的協議中,讓需要實現它的類實現helloWorld這個協議。
注意點
接口盡量小,但是要有限度。對接口進行細化可以提高程序設計靈活性是不掙的事實,但是如果過小,則會造成接口數量過多,使設計復雜化。所以一定要適度。
為依賴接口的類定制服務,只暴露給調用的類它需要的方法,它不需要的方法則隱藏起來。只有專注地為一個模塊提供定制服務,才能建立最小的依賴關系。
提高內聚,減少對外交互。使接口用最少的方法去完成最多的事情。
運用接口隔離原則,一定要適度,接口設計的過大或過小都不好。設計接口的時候,只有多花些時間去思考和籌劃,才能準確地實踐這一原則。