接口分离原则
- 将一些大的接口细化成一些小的接口提供使用,即多个特定的客户端接口要好于一个通用性的总接口。
- 但是注意的是:接口的粒度也不能太小。如果过小则会造成接口数量过多,使设计复杂化
优点
- 避免同一个接口里面包含不同类职责的方法,接口责任划分更加明确,符合高内聚低耦合的思想。
代码讲解
- 下面我们通过汽车的例子讲解一下接口隔离原则
需求点
- 一辆车能够奔跑、能刹车、能加速、安全气囊、烧油。
- 一辆电动车能够奔跑、能刹车、能加速、安全气囊、用电。
不好的设计
- 代理 将能力作为接口放到一个类中。
------------ NormalCarProtocol.h -------------
@protocol NormalCarProtocol <NSObject>
- (void)run; //奔跑
- (void)brake; //刹车
- (void)speed; //加速
- (void)safeAirCell; //安全气囊
//@optional
- (void)useGasoline; //烧油
- (void)useElectricPower; //用电
@end
- 普通汽车准守协议
-------- 普通的汽车-----
- 准守这个协议
@interface NormalCar : NSObject<NormalCarProtocol>
@end
----- 实现协议的能力 -----
@implementation NormalCar
- (void)run{
NSLog(@"奔跑");
}
- (void)brake{
NSLog(@"刹车");
}
- (void)speed{
NSLog(@"加速");
}
- (void)safeAirCell{
NSLog(@"安全气囊");
}
- (void)useGasoline{
NSLog(@"使用汽油");
}
- 这个协议能力我有但是我不要实现
- (void)useElectricPower{
return;
}
@end
- 电动汽车
----- 电动汽车 ---- 也准守这个协议
@interface Electrocar : NSObject<NormalCarProtocol>
@end
- 实现这些能力
@implementation Electrocar
- (void)run{
NSLog(@"奔跑");
}
- (void)brake{
NSLog(@"刹车");
}
- (void)speed{
NSLog(@"加速");
}
- (void)safeAirCell{
NSLog(@"安全气囊");
}
- 使用汽油这个能力是失效的。
- (void)useGasoline{
return;
}
- (void)useElectricPower{
NSLog(@"使用电力");
}
@end
我们发现,并不是所有汽车都必须要实现NormalCarProtocol里面的所有方法。由于接口方法的设计造成了冗余,因此该设计不符合接口隔离原则。
好的设计
- 把用电还是用汽油的接口分出去变成 单独的一种协议
@protocol PowerProtocol <NSObject>
- (void)Power; //动力
@end
- 让汽车准守两个协议
@interface NormalCar : NSObject<NormalCarProtocol,PowerProtocol>
----- 实现动力方法 ----
- (void)Power{
NSLog(@"使用汽油");
}
@interface Electrocar : NSObject<NormalCarProtocol,PowerProtocol>
----- 实现动力方法 ----
- (void)Power{
NSLog(@"使用电力");
}
因为我们把不同职责的接口拆开,使得接口的责任更加清晰,简洁明了。不同的汽车可以根据自己的需求遵循所需要的接口来以自己的方式实现。
而且今后如果还有其他的功能比如播放音乐相关的方法,可以再加一个协议,避免了接口的臃肿,同时也提高了程序的内聚性。 加一个音乐的协议 两种汽车准守协议完成功能。 #思考
- 现在又多了一台老爷爷代步车,没有播放音乐。
- 是不是就是 汽车三个协议,电动车三个协议,代步车两个协议。分别实现协议中的方法就可以了。
UML 类图对比
- 未实践接口分离原则:拥有无效的方法
- 实践了接口分离原则:
通过遵守接口分离原则,接口的设计变得更加简洁,而且各种客户类不需要实现自己不需要实现的接口。
如何实践
-
在设计接口时,尤其是在向现有的接口添加方法时,我们需要仔细斟酌这些方法是否是处理同一类任务的:如果是则可以放在一起;如果不是则需要做拆分。
-
做iOS开发的朋友对UITableView的UITableViewDelegate和UITableViewDataSource这两个协议应该会非常熟悉。
-
这两个协议里的方法都是与UITableView相关的,但iOS SDK的设计者却把这些方法放在不同的两个协议中。原因就是这两个协议所包含的方法所处理的任务是不同的两种:
-
UITableViewDelegate:含有的方法是UITableView的实例告知其代理一些点击事件的方法,即事件的传递,方向是从UITableView的实例到其代理。
-
UITableViewDataSource:含有的方法是UITableView的代理传给UITableView一些必要数据供UITableView展示出来,即数据的传递,方向是从UITableView的代理到UITableView。
-
很显然,UITableView协议的设计者很好地实践了接口分离的原则,值得我们大家学习。