设计模式六大原则(四)----接口隔离原则

简介: 设计模式六大原则(四)----接口隔离原则

一. 接口隔离原则的定义


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.


类间的依赖关系应建立在最小的接口上。

也就是说: 接口尽量细化,接口中的方法尽量少


二. 接口隔离原则和单一职责原则



从功能上来看,接口隔离原则和单一职责原则都是为了提高类的内聚, 降低类之间的耦合, 体现了封装的思想。但二者还是有区别的。


(1)从原则约束来看: 接口隔离原则更关注的是接口依赖程度的隔离;而单一职责原则更加注重的是接口职责的划分。

(2)从接口的细化程度来看: 单一职责原则对接口的划分更加精细,而接口隔离原则注重的是相同功能的接口的隔离。接口隔离里面的最小接口有时可以是多个单一职责的公共接口。


(3)单一职责原则更加偏向对业务的约束: 接口隔离原则更加偏向设计架构的约束。这个应该好理解,职责是根据业务功能来划分的,所以单一原则更加偏向业务;而接口隔离更多是为了“高内聚”,偏向架构的设计。


三. 接口隔离原则的优点


接口隔离原则是为了约束接口、降低类对接口的依赖性,遵循接口隔离原则有以下 5 个优点。


  1. 将臃肿庞大的接口分解为多个粒度小的接口,可以预防外来变更的扩散,提高系统的灵活性和可维护性。
  2. 接口隔离提高了系统的内聚性,减少了对外交互,降低了系统的耦合性。
  3. 如果接口的粒度大小定义合理,能够保证系统的稳定性;然而,如果定义过小,则会造成接口数量过多,使设计复杂化;如果定义太大,灵活性降低,无法提供定制服务,给整体项目带来无法预料的风险。
  4. 使用多个专门的接口能够体现对象的层次,因为可以通过接口的继承,实现对总接口的定义。
  5. 能减少项目工程中的代码冗余。过大的大接口里面通常放置许多不用的方法,当实现这个接口的时候,被迫设计冗余的代码。


四. 接口隔离原则的实现方法



在具体应用接口隔离原则时,应该根据以下几个规则来衡量。


1)接口要尽量小

不能出现Fat Interface;但是要有限度,首先不能违反单一职责原则(不能一个接口对应半个职责)。

2)接口要高内聚

在接口中尽量少公布public方法。

接口是对外的承诺,承诺越少对系统的开发越有利。

3)定制服务

只提供访问者需要的方法。例如,为管理员提供IComplexSearcher接口,为公网提供ISimpleSearcher接口。

4)接口的设计是有限度的

了解环境,拒绝盲从。每个项目或产品都有选定的环境因素,环境不同,接口拆分的标准就不同, 需要深入了解业务逻辑。


五. 接口隔离原则的建议



  1. 一个接口只服务于一个子模块或业务逻辑;
  2. 通过业务逻辑压缩接口中的public方法;
  3. 已被污染了的接口,尽量去修改;若变更的风险较大,则采用适配器模式转化处理;
  4. 拒绝盲从


五. 案例分析



下面以学生成绩管理为例来说明接口隔离原则:

分析:学生成绩管理程序一般包含查询成绩、新增成绩、删除成绩、修改成绩、计算总分、计算平均分、打印成绩信息等功能,通常我们会怎么做呢?


一: 最初的设计


通常我们设计接口的方式如下:

public interface IStudentScore {
    // 查询成绩
    public void queryScore();
    // 修改成绩
    public void updateScore();
    // 添加成绩
    public void saveScore();
    // 删除成绩
    public void delete();
    // 计算总分
    public double sum();
    // 计算平均分
    public double avg();
    // 打印成绩单
    public void printScore();
}

我们会吧所有的功能都放在一个接口里面. 这会产生什么样的问题呢?


首先, 接口的方法很多, 不利于扩展. 比如: 学生只有查看成绩,打印成绩单的权限, 没有增删改的权限; 老师拥有所有的权限.


查询成绩单:

package com.lxl.www.designPatterns.sixPrinciple.interfaceSegregationPrinciple.score;
public class QueryScore implements IStudentScore{
    @Override
    public void queryScore() {
        // 查询成绩
    }
    @Override
    public void updateScore() {
         // 没有权限
    }
    @Override
    public void saveScore() {
        // 没有权限
    }
    @Override
    public void delete() {
        // 没有权限
    }
    @Override
    public double sum() {
        // 没有权限
        return 0;
    }
    @Override
    public double avg() {
        // 没有权限
        return 0;
    }
    @Override
    public void printScore() {
        //打印成绩单
    }
}

操作成绩单

package com.lxl.www.designPatterns.sixPrinciple.interfaceSegregationPrinciple.score;
public class Operate implements IStudentScore{
    @Override
    public void queryScore() {
    }
    @Override
    public void updateScore() {
    }
    @Override
    public void saveScore() {
    }
    @Override
    public void delete() {
    }
    @Override
    public double sum() {
        return 0;
    }
    @Override
    public double avg() {
        return 0;
    }
    @Override
    public void printScore() {
    }
}

可以看出问题. 查询成绩单, 我们只会用到两个方法, 可是因为实现了接口, 不得不重写所有的方法.


如果这时候增加需求--发送给家长, 只有老师才有这个权限, 学生没有这个权限. 可是, 在接口中增加一个抽象方法以后, 所有的实现类都要重写这个方法. 这就违背了开闭原则.


2. 使用接口隔离原则的设计


采用接口隔离原则设计的接口, UML图如下:


public interface IQueryScore {
    // 查询成绩
    public void queryScore();
    // 打印成绩单
    public void printScore();
}
public interface IOperateScore {
    // 修改成绩
    public void updateScore();
    // 添加成绩
    public void saveScore();
    // 删除成绩
    public void delete();
    // 计算总分
    public double sum();
    // 计算平均分
    public double avg();
}
public class StudentOperate implements IQueryScore{
    @Override
    public void queryScore() {
        // 查询成绩
    }
    @Override
    public void printScore() {
        //打印成绩单
    }
}
public class TeacherOperate implements IQueryScore, IOperateScore{
    @Override
    public void queryScore() {
    }
    @Override
    public void updateScore() {
    }
    @Override
    public void saveScore() {
    }
    @Override
    public void delete() {
    }
    @Override
    public double sum() {
        return 0;
    }
    @Override
    public double avg() {
        return 0;
    }
    @Override
    public void printScore() {
    }
}

我们将原来的一个接口进行了接口拆分. 分为查询接口和操作接口. 这样学生端就不需要重写和他不相关的接口了.


如果将这些功能全部放到一个接口中显然不太合理,正确的做法是将它们分别放在输入模块、统计模块和打印模块等 3 个模块中,其类图如图 1 所示

相关文章
|
5月前
|
设计模式 Java 程序员
|
6月前
|
设计模式 Java 测试技术
Java设计模式七大原则-接口隔离原则
Java设计模式七大原则-接口隔离原则
36 0
|
1月前
|
设计模式 关系型数据库
设计模式的六大原则:理解设计模式的关键思想和应用
设计模式的六大原则:理解设计模式的关键思想和应用
18 2
|
1月前
|
设计模式
【设计模式】1、设计模式七大原则
【设计模式】1、设计模式七大原则
16 0
|
3月前
|
设计模式 存储 NoSQL
【设计模式】软件设计原则-单一职责原则
【1月更文挑战第12天】【设计模式】软件设计原则-单一职责原则
|
3月前
|
设计模式 关系型数据库
【设计模式】软件设置原则-开闭原则
【1月更文挑战第12天】【设计模式】软件设置原则-开闭原则
|
4月前
|
设计模式 Java
Java设计模式七大原则之依赖倒置原则
Java设计模式七大原则之依赖倒置原则
29 0
|
4月前
|
设计模式 Java 开发者
Java设计模式七大原则之里氏替换原则
Java设计模式七大原则之里氏替换原则
27 0
|
4月前
|
设计模式 安全 Java
Java设计模式七大原则之开闭原则
Java设计模式七大原则之开闭原则
34 0
|
5月前
|
设计模式 关系型数据库
设计模式原则(下)
设计模式原则(下)