文章目录
- 一、定义:接口隔离原则
- 二、模拟场景:接口隔离原则
- 三、违背方案:接口隔离原则
-
- [3.1 工程结构](#3.1 工程结构)
- [3.2 英雄技能调用](#3.2 英雄技能调用)
-
- [3.2.1 英雄技能接口](#3.2.1 英雄技能接口)
- [3.2.2 英雄:后裔](#3.2.2 英雄:后裔)
- [3.2.3 英雄:廉颇](#3.2.3 英雄:廉颇)
- [3.3 单元测试](#3.3 单元测试)
- 四、改善代码:接口隔离原则
-
- [4.1 工程结构](#4.1 工程结构)
- [4.2 英雄分配英雄技能](#4.2 英雄分配英雄技能)
-
- [4.2.1 射箭接口](#4.2.1 射箭接口)
- [4.2.2 隐袭接口](#4.2.2 隐袭接口)
- [4.2.3 沉默](#4.2.3 沉默)
- [4.2.4 晕眩](#4.2.4 晕眩)
- [4.2.5 英雄后裔的实现](#4.2.5 英雄后裔的实现)
- [4.2.6 英雄廉颇的实现](#4.2.6 英雄廉颇的实现)
- [4.3 单元测试](#4.3 单元测试)
一、定义:接口隔离原则
- 接口隔离原则 :
Interface Segregation Principle,ISP
。- 客户端不应该被迫依赖于它不使用的方法。
- 另一个定义:一个类对另一个类的依赖应该建立在最小的接口上。
- 接口隔离原则要求程序员尽量将臃肿庞大的接口拆分成更小的和更具体的接口,让接口只包含客户感兴趣的方法。
- 接口隔离是为了 高内聚、低耦合 。
- 在实际的业务开发中,通常会先定义好需要开发的接口,并由各个服务类实现。
- 但如果没有经过考虑和设计,就很可能造成一个接口中包括众多的接口方法,而这些接口并不一定在每一个类中都需要实现。
- 这样的接口很难维护,也不易于扩展,每一次修改验证都有潜在的风险。
- 在具体应用接口隔离原则时,应该根据一下几个规则衡量。
- 接口尽量小,但是要有限度。一个接口只服务于一个子模块或业务逻辑。
- 为依赖接口的类定制服务。只提供调用者需要的方法,屏蔽不需要的方法。
- 了解环境,拒绝盲从。每个项目或产品都有选定的环境因素,环境不同,接口拆分的标准就不同,要深入了解业务逻辑。
- 提高内聚,减少对外交互。让接口用最少的方法完成最多的事情。
二、模拟场景:接口隔离原则
- 《王者荣耀》里有很多英雄,可以分为射手、战士、刺客等,每个英雄有三种技能。
- 这些技能该如何定义,让每个英雄实现相应的技能效果呢?
三、违背方案:接口隔离原则
3.1 工程结构
jsx
design-1.5-0
|------src
|------main
|--java
|--com.lino.design
|--HeroHouYi.java
|--HeroLianPo.java
|--ISkill.java
|------test
|--java
|--com.lino.design.test
|--ApiTest.java
3.2 英雄技能调用
3.2.1 英雄技能接口
ISkill.java
java
package com.lino.design;
/**
* @description: 英雄技能接口
*/
public interface ISkill {
/**
* 射箭
*/
void doArchery();
/**
* 隐袭
*/
void doInvisible();
/**
* 沉默
*/
void doSilent();
/**
* 眩晕
*/
void doVertigo();
}
- 定义一个技能接口,实现的英雄都需要实现这个接口,进而实现自己的技能。
- 这里提供了四个技能的接口,包括射箭、隐袭、沉默、晕眩,每个英雄都实现这个接口。接下来实现两个英雄:后裔和廉颇。
3.2.2 英雄:后裔
HeroHouYi.java
java
package com.lino.design;
/**
* @description: 英雄:后裔
*/
public class HeroHouYi implements ISkill {
@Override
public void doArchery() {
System.out.println("后裔的灼日之矢");
}
@Override
public void doInvisible() {
System.out.println("后裔的隐身技能");
}
@Override
public void doSilent() {
System.out.println("后裔的沉默技能");
}
@Override
public void doVertigo() {
// 无此技能的实现
}
}
- 在英雄后裔的类中,实现了三个技能,最后一个晕眩的技能是不需要实现的。
3.2.3 英雄:廉颇
HeroLianPo.java
java
package com.lino.design;
/**
* @description: 英雄:廉颇
*/
public class HeroLianPo implements ISkill {
@Override
public void doArchery() {
// 无此技能的实现
}
@Override
public void doInvisible() {
System.out.println("廉颇的隐身技能");
}
@Override
public void doSilent() {
System.out.println("廉颇的沉默技能");
}
@Override
public void doVertigo() {
System.out.println("廉颇的眩晕技能");
}
}
- 在英雄廉颇的类中,同样只实现了三个技能,有一个射箭的技能没有实现。
3.3 单元测试
ApiTest.java
java
@Test
public void test_ISKill() {
// 后裔
HeroHouYi heroHouYi = new HeroHouYi();
heroHouYi.doArchery();
// 廉颇
HeroLianPo heroLianPo = new HeroLianPo();
heroLianPo.doInvisible();
}
测试结果
java
后裔的灼日之矢
廉颇的隐身技能
- 综上,每个英雄的实现类里都有一个和自己无关的接口实现类,非常不符合设计模式,也不易于维护。
- 因为不仅无法控制外部的调用,还需要维护对应的文档,来说明这个接口不需要实现。如果由更多这样的接口,就会变得非常麻烦。
四、改善代码:接口隔离原则
4.1 工程结构
jsx
design-1.5-1
|------src
|------main
|--java
|--com.lino.design
|--impl
| |-HeroHouYi.java
| |-HeroLianPo.java
|--ISkillArchery.java
|--ISkillInvisible.java
|--ISkillSilent.java
|--ISkillVertigo.java
|------test
|--java
|--com.lino.design.test
|--ApiTest.java
4.2 英雄分配英雄技能
- 按照接口隔离原则的约定,应该在确保合理的情况下,把接口细分。保证一个松散的结构,也就是把技能拆分出来,每个英雄都可以按需继承实现。
- 接下来分别定义四个技能接口,包括:
- 射箭:
ISkillArchery
- 隐身:
ISkillInvisible
- 沉默:
ISkillSilent
- 晕眩:
ISkillVertigo
- 射箭:
4.2.1 射箭接口
ISkillArchery.java
java
package com.lino.design;
/**
* @description: 射箭技能
*/
public interface ISkillArchery {
/**
* 射箭
*/
void doArchery();
}
4.2.2 隐袭接口
ISkillInvisible.java
java
package com.lino.design;
/**
* @description: 影袭技能
*/
public interface ISkillInvisible {
/**
* 隐袭
*/
void doInvisible();
}
4.2.3 沉默
ISkillSilent.java
java
package com.lino.design;
/**
* @description: 沉默技能
*/
public interface ISkillSilent {
/**
* 沉默
*/
void doSilent();
}
4.2.4 晕眩
ISkillSilent.java
java
package com.lino.design;
/**
* @description: 晕眩技能
*/
public interface ISkillVertigo {
/**
* 眩晕
*/
void doVertigo();
}
4.2.5 英雄后裔的实现
HeroHouYi.java
java
package com.lino.design.impl;
import com.lino.design.ISkillArchery;
import com.lino.design.ISkillInvisible;
import com.lino.design.ISkillSilent;
/**
* @description: 英雄:后裔
*/
public class HeroHouYi implements ISkillArchery, ISkillInvisible, ISkillSilent {
@Override
public void doArchery() {
System.out.println("后裔的灼日之矢");
}
@Override
public void doInvisible() {
System.out.println("后裔的隐身技能");
}
@Override
public void doSilent() {
System.out.println("后裔的沉默技能");
}
}
4.2.6 英雄廉颇的实现
HeroLianPo.java
java
package com.lino.design.impl;
import com.lino.design.ISkillInvisible;
import com.lino.design.ISkillSilent;
import com.lino.design.ISkillVertigo;
/**
* @description: 英雄:廉颇
*/
public class HeroLianPo implements ISkillInvisible, ISkillSilent, ISkillVertigo {
@Override
public void doInvisible() {
System.out.println("廉颇的隐身技能");
}
@Override
public void doSilent() {
System.out.println("廉颇的沉默技能");
}
@Override
public void doVertigo() {
System.out.println("廉颇的眩晕技能");
}
}
4.3 单元测试
ApiTest.java
java
@Test
public void test_ISKill() {
// 后裔
HeroHouYi heroHouYi = new HeroHouYi();
heroHouYi.doArchery();
// 廉颇
HeroLianPo heroLianPo = new HeroLianPo();
heroLianPo.doInvisible();
}
测试结果
java
后裔的灼日之矢
廉颇的隐身技能
- 现在可以看到这两个英雄的类都按需实现了自己需要的技能接口。
- 这样的实现方式就可以避免一些本身不属于自己的技能还需要不断地用文档的方式进行维护,同时提高了代码的可靠性,在别人接手或者修改时,可以降低开发成本和维护风险。