美文网首页
设计模式六大原则(四)----接口隔离原则

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

作者: 不想当厨子的裁缝不是好司机II | 来源:发表于2021-06-17 14:07 被阅读0次

    一. 接口隔离原则的定义

    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 所示

    相关文章

      网友评论

          本文标题:设计模式六大原则(四)----接口隔离原则

          本文链接:https://www.haomeiwen.com/subject/baoaeltx.html