接口隔离原则,英文缩写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.
类之间的依赖应该建立在最小的接口上面。
简单点说,客户端需要什么功能,就提供什么接口,对于客户端不需要的接口不应该强行要求其实现;类之间的依赖应该建立在最小的接口上面,这里最小的粒度取决于单一职责原则的划分。
假设我们有一堆几何体(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的面积与体积之和,而不是依靠改动已有shapeInterface
或solidShapeInterface
这两个接口,那又该怎么实现呢?
你可以创建另一个接口,名字可以是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})
}