如何理解接口隔离原则
Interface Segregation Principle
Clients should not be forced to depend upon interfaces that they do not use。
即接口的调用者或者使用者,不应该被强迫依赖它不需要的接口。
这个原则核心在于理解其中的“接口”二字,我们可以把“接口”理解为下面三种类型
一组 API 接口集合
以下代码是我们平时常见的对用户信息的处理,比如注册、登陆、根据手机号获取用户信息等。假设我们现在有一个需求,需要实现一个删除用户的功能,在不考虑接口隔离的情况下,就是直接在UserService
中添加一个deleteUserByCellphone
或者deleteUserByCellphone
,但这样会导致一个问题,所有可以调用获取信息的地方,也可以调用删除的逻辑,很有可能导致误删用户。也就是“获取用户信息接口的调用者,不应该被强迫依赖它不需要的删除用户的接口”。
public interface UserService {
boolean register(String cellphone, String password);
boolean login(String cellphone, String password);
UserInfo getUserInfoById(long id);
UserInfo getUserInfoByCellphone(String cellphone);
}
在接口隔离的思想下。将删除的接口独立在一个RestrictedUserService
接口中。这样就能保证,删除的接口,只会被需要实现删除逻辑地方使用。至于用户注册的场景,并不会被强迫依赖不需要的删除接口。
public interface UserService {
boolean register(String cellphone, String password);
boolean login(String cellphone, String password);
UserInfo getUserInfoById(long id);
UserInfo getUserInfoByCellphone(String cellphone);
}
public interface RestrictedUserService {
boolean deleteUserByCellphone(String cellphone);
boolean deleteUserById(long id);
}
public class UserServiceImpl implements UserService, RestrictedUserService {
// ...省略实现代码...
}
单个 API 接口或函数
在这样的场景下,函数的设计要功能单一,不要将多个不同的功能逻辑在一个函数中实现。
public class Statistics {
private Long max;
private Long min;
private Long average;
private Long sum;
private Long percentile99;
private Long percentile999;
//...省略constructor/getter/setter等方法...
}
public Statistics count(Collection<Long> dataSet) {
Statistics statistics = new Statistics();
//...省略计算逻辑...
return statistics;
}
上面这个函数解决的是统计需求,假设在统计时,Statistics 中的所有属性都用到了,那么 count 就是一个功能单一的接口。但假设统计信息有的只涉及到 max、min,有的只涉及到 average、sum。那么 count 的职责就不算单一。应该拆分成更小粒度的函数。
public Long max(Collection<Long> dataSet) { //... }
public Long min(Collection<Long> dataSet) { //... }
public Long average(Colletion<Long> dataSet) { //... }
// ...省略其他统计函数...
接口隔离和单一职责的区别在于,单一职责针对的是模块、类、接口的设计。而接口隔离原则相对于单一职责原则,更加侧重于接口的设计。它提供了一种评判接口是否单一的标准:通过调用者如何使用接口来间接地判定。如果调用者只使用部分接口或接口的部分功能,那接口的设计就不够职责单一