今天我们来聊聊S.O.L.I.D原则
中的I:Interface Segregation Principle(ISP)
,接口隔离原则。
ISP 简介
接口隔离原则(Interface Segregation Principle)
的定义是:类间的依赖关系应该建立在最小的接口上。
嗯~~我们从字面上理解下或许更直白一些。所谓“接口隔离”,就是把接口隔离开。接口怎么隔离开呢?当然是接口间相互不影响咯!
接口里面是什么呢?当然封装的是抽象方法,或者我们称为行为。
接口间相互不影响,那就是两个或多个接口中的方法不相互影响。
于是,就是我们经常说到的:一个接口应该拥有尽可能少的行为,使其精简单一。
如果你实在不知道接口隔离原则怎么用?那么最~最~最简单的做法:一个接口里,一个行为方法!
PS:这不一定通用!也不一定完全适合!但它隔离得足够清楚!
大家不是常说:接口的内容一定要尽可能地小,能有多小就多小。
我的接口中就只有一个行为方法了,没有更小的啦~😄😄
ISP 示例
接口隔离原则真的足够简单。根据上面对定义的解释,我们可以很快的对下面的示例进行修改。
假设您正在设计一个系统来管理各种类型的设备,例如打印机、扫描仪和传真机。您可能想创建一个名为 Device 的接口,如下所示:
public interface Device
{
void print();
void scan();
void fax();
}
按照我们实际场景中,办公室的电脑都能连接到打印机,进行文件打印。打印机的文件打印行为正好在 Device 接口中有。于是:
public class Printer implements Device {
@Override
void print() {
System.out.println("文件打印!");
}
@Override
void scan(){
System.out.println("文件扫描!");
}
@Override
void fax(){
}
}
由于我们实例了 Device 接口,我们就不能只重写 print()和scan()方法。还必须重写 fax() 的方法,即使我们的打印机没有发传真的功能。
同理,简单的传真机,也经常只有发传真的行为。
public class FaxMachine implements Device {
@Override
void print() {
}
@Override
void scan(){
}
@Override
void fax(){
System.out.println("传真文件!");
}
}
我们的 FaxMachine 也重写了全部方法,即使它没有打印和扫描的功能。
可以看出,无论是 Printer 类,还是 FaxMachine 类,它们依赖的行为方法都来之一个比较大的接口 Device。
这违背了我们接口隔离原则的定义:类间的依赖关系应该建立在最小的接口上。
对于 Printer 类来说,最小的接口应该只包含 print() 和 scan() 方法就好;对于 FaxMachine 类来说,最小的接口只需要包含 fax()。
于是,我们就可以基于接口隔离原则对代码进行重构。
先拆出满足 Printer 类的最小接口:
public interface IPrinter
{
void print();
void scan();
}
先拆出满足 Printer 类的最小接口:
public interface IFax
{
void fax();
}
Printer 类和 FaxMachine 类就可以分别实现它们啦~
public class Printer implements IPrinter {
@Override
void print() {
System.out.println("文件打印!");
}
@Override
void scan(){
System.out.println("文件扫描!");
}
}
原来 Printer 类中不需要的行为方法scan()
就可以删除了。
同理,FaxMachine 类也是如此。
public class FaxMachine implements IFax {
@Override
void fax(){
System.out.println("传真文件!");
}
}
再进一步
我们知道,不是每一个打印机都有打印和扫描功能的。小二哥家里的的打印机,就没有扫描功能。
怎么办?继续拆!
public interface IPrinter
{
void scan();
}
public interface IScanner
{
void scan();
}
把扫描功能拆出去后,打印接口 IPrinter 就只有打印功能了。
完美
等等~~ 我们最开始的打印机 Printer 类可是有打印和扫描功能的,你不能给咱们搞丢一个呀!赶紧补起来!
public class Printer implements IPrinter,IScanner {
@Override
void print() {
System.out.println("文件打印!");
}
@Override
void scan(){
System.out.println("文件扫描!");
}
}
依赖两个接口(IPrinter 和 IScanner)就好啦~
总结
接口隔离原则,使用起来非常简单方便。它提倡不要将一个大而全的接口扔给使用者,而是将每个使用者关注的接口进行隔离。
换句话说,就是:对于不同的功能的模块分别使用不同接口,而不是使用同一个通用的接口。
如果你在 implements 某个接口的时候,发现该接口中的一些方法用不上,那你就需要对接口进行拆分!
PS:最无脑的操作就是,把接口拆成一个方法对应一个接口!
接口隔离原则,不是无缘无故的存在的。它和之前的单一职责原则和里氏替换原则紧密相关。
一个方法,是不是代表一个行为?单个行为,就是单一的职责。这是不是就体现了单一职责原则。
如果两个对象的行为不一致,它们就不能替换。我们强行把 Printer 类和 FaxMachine 类都定义为 Device 的衍生类,但它们都不能全面提供 Device 拥有的打印、扫描和发传真的功能。
S.O.L.I.D原则
中的各个原则间是相互有关联的。具体的关系,我们后面会详细讲解,敬请期待~