陆.1.2 接口隔离原则

01.什么是接口隔离原则

接口隔离原则,英文缩写ISP,全称 Interface Segregation Principle

定义一:

Clients should not be forced to depend upon interfaces that they don't use. 不应该强行要求客户端依赖于它们不用的接口。

定义二:

The dependency of one class to another one should depend on the smallest possible interface. 类之间的依赖应该建立在最小的接口上面。

简单点说,客户端需要什么功能,就提供什么接口,对于客户端不需要的接口不应该强行要求其实现;类之间的依赖应该建立在最小的接口上面,这里最小的粒度取决于单一职责原则的划分。

02.举例说明

假设我们有一堆几何体(shape),除了计算它们的面积,我们还想计算出它们的体积。我们可以这样用JavaScript模拟做一个interface(接口):

//state为几何体的状态参数集合,是一个json对象
const shapeInterface = (state) => ({
type: 'shapeInterface',//接口名
area: () => state.area(state),//计算面积
volume: () => state.volume(state)//计算体积
})

by the way,用JavaScript简单模拟面向对象语言中的interface特性,可以参阅本书 用JavaScript实现接口

如果按上面的代码实现接口,那么,我们创建的任何shape都必须实现计算体积的方法。但是我们知道正方形是平面的,并且平面的shape没有体积,因此此接口将强制正方形的构造函数实现计算体积的方法。

接口隔离原则对此表示反对。怎么办呢?你可以创建另一个称为solidShapeInterface的接口,该接口具有计算体积的方法,诸如立方体等立体shape都可以实现此接口:

//定义计算面积的接口
const shapeInterface = (state) => ({
type: 'shapeInterface',
area: () => state.area(state)
})
//定义计算体积的接口
const solidShapeInterface = (state) => ({
type: 'solidShapeInterface',
volume: () => state.volume(state)
})
//定义一个立方体
const cubo = (length) => {
//state
const proto = {
//属性
type : 'Cubo',
length,
//方法
area : (args) => Math.pow(args.length, 2) * 6,
volume : (args) => Math.pow(args.length, 3)
}
const basics = shapeInterface(proto) //实现计算面积的接口
const complex = solidShapeInterface(proto)//实现计算体积的接口
//利用Object.assign组合出一个新的类,这个类实现了上面2种接口
const composite = Object.assign({}, basics, complex)
//最后传入参数,返回新的类的实例
return Object.assign(Object.create(composite), {length})
}

上面代码是一种相对较好的方法,能够计算面积,也能计算体积。但是要注意的是,如果想计算shape的面积与体积之和,而不是依靠改动已有shapeInterfacesolidShapeInterface这两个接口,那又该怎么实现呢?

你可以创建另一个接口,名字可以是manageShapeInterface,并在平面和立体的shape上都实现它,这种方式的代码如下:

const manageShapeInterface = (fn) => ({
type: 'manageShapeInterface',
calculate: () => fn()
})
//平面圆
const circle = (radius) => {
const proto = {
radius,
type: 'Circle',
area: (args) => Math.PI * Math.pow(args.radius, 2)
}
const basics = shapeInterface(proto)
const abstraccion = manageShapeInterface(() => basics.area())
const composite = Object.assign({}, basics, abstraccion)
return Object.assign(Object.create(composite), {radius})
}
//立方体
const cubo = (length) => {
const proto = {
length,
type : 'Cubo',
area : (args) => Math.pow(args.length, 2) * 6,
volume : (args) => Math.pow(args.length, 3)
}
const basics = shapeInterface(proto)
const complex = solidShapeInterface(proto)
//关键语句,传入一个函数,让该函数实现具体的逻辑
const abstraccion = manageShapeInterface(
() => basics.area() + complex.volume()
)
const composite = Object.assign({}, basics, abstraccion)
return Object.assign(Object.create(composite), {length})
}