为了账号安全,请及时绑定邮箱和手机立即绑定
慕课专栏

目录

索引目录

设计模式深度解析34讲

原价 ¥ 68.00

立即订阅
03 七大设计原则(下)—— 无规矩不方圆
更新时间:2019-11-26 09:43:46
智慧,不是死的默念,而是生的沉思。——斯宾诺莎

1、基本概念

本节继续介绍设计模式的七大原则的基本概念,上一节重要讲了开闭原则、单一职责原则、里氏替换原则、依赖倒置原则,这一节我们主要了解下接口隔离原则、迪米特法则以及合成复用原则。

本节以介绍基本概念为主,其中会加入部分演示代码、uml 类图讲解,能理解基本概念即可。后续章节设计模式的讲解会详细介绍这些原则的应用。

本节主要内容有:

  • 什么是接口隔离原则、迪米特法则以及合成复用原则

  • 为何要遵循这些原则

2、接口隔离原则

接口隔离原则(Interface Segregation Principle,ISP)的定义是客户端不应该依赖它不需要的接口,类间的依赖关系应该建立在最小的接口上。 简单来说就是建立单一的接口,不要建立臃肿庞大的接口。也就是接口尽量细化,同时接口中的方法尽量少,保持接口纯洁性。

我们所讲的接口主要分为两大类,一是实例接口,比如使用 new 关键字产生一种实例,被 new 的类就是实例类的接口。从这个角度出发的话,java 中的类其实也是一种接口。二是类接口,java 中常常使用 interface 关键字定义。

举个栗子来说,我们使用接口 IPrettyGirl 来描述美女,刚开始类图可能描述如下:

图片描述
但是发现该接口中包含对美女的外观描述、内在美描述等,几乎将美女的所有特性全部纳入,这显然不是一个很好的设计规范,比如在唐朝,在那个以丰腴为美的时代对美的理解就不同,就会出现单纯 goodLooking 过关就是美女的结果,所以这里我们需要将接口隔离拆分。将一个接口拓展为两个,增加系统灵活性及可维护性。 图片描述
这里我们将美女接口拆分为内在美、外在美两个接口,系统灵活性提高了,另外接口间还能使用继承实现聚合,系统拓展性也得到了增强。

接口隔离原则总结:

  • 接口尽量粒度化,保持接口纯洁性

  • 接口要高内聚,即减少对外交互

3、迪米特法则

迪米特法则(Law of Demeter,LOD),有时候也叫做最少知识原则(Least Knowledge Principle,LKP),它的定义是:一个软件实体应当尽可能少地与其它实体发生相互作用。迪米特法则的初衷在于降低类之间的耦合。

举个栗子,拿教师点名来讲,体育老师需要清点班上学生人数,教师一般不是自己亲自去数,而是委托组长或班长等人去清点,即教师通过下达命令至班长要求清点人数:

public class Girl {

}

public class GroupLeader {

    private final List<Girl> girls;

    public GroupLeader(List<Girl> girls) {
        this.girls = girls;
    }

    public void countGirls() {
        System.out.println("The sum of girls is " + girls.size());
    }
}

public class Teacher {

    public void command(GroupLeader leader){
        leader.countGirls();
    }
}

public class Main {

    public static void main(String[] args) throws Exception {
        Teacher teacher = new Teacher();
        GroupLeader groupLeader = new GroupLeader(Arrays.asList(new Girl(), new Girl()));
        teacher.command(groupLeader);
    }
}

图片描述

上述例子中,如果去掉 GroupLeader 这个中间人角色,教师就会直接去清点人数,这样做会违反迪米特法则。

迪米特法则总结:

  • 类定义时尽量内敛,少使用 public 权限修饰符,尽量使用 private、protected 等。

4、合成复用原则

合成复用原则是通过将已有的对象纳入新对象中,作为新对象的成员对象来实现的,新对象可以调用已有对象的功能,从而达到复用。 原则是尽量首先使用合成 / 聚合的方式,而不是使用继承。

合成和聚合都是关联的特殊种类。合成是值的聚合(Aggregation by Value),而复合是引用的聚合(Aggregation by Reference)。

都知道,类之间有三种基本关系,分别是:关联(聚合和组合)、泛化(与继承同一概念)、依赖。

这里我们提一下关联关系,客观来讲,大千世界中的两个实体之间总是有着千丝万缕的关系,归纳到软件系统中就是两个类之间必然存在关联关系。如果一个类单向依赖另一个类,那么它们之间就是单向关联。如果彼此依赖,则为相互依赖,即双向关联。

关联关系包括两种特例:聚合和组合。聚合,用来表示整体与部分的关系或者 “拥有” 关系。其中,代表部分的对象可能会被代表多个整体的对象所拥有,但是并不一定会随着整体对象的销毁而销毁,部分的生命周期可能会超越整体。好比班级和学生,班级销毁或解散后学生还是存在的,学生可以继续存在某个培训机构或步入社会,生命周期不同于班级甚至大于班级。

合成,用来表示一种强得多的 “拥有” 关系。其中,部分和整体的生命周期是一致的,一个合成的新的对象完全拥有对其组成部分的支配权,包括创建和泯灭。好比人的各个器官组成人一样,一旦某个器官衰竭,人也不复存在,这是一种 “强” 关联。

图片描述

如上图所示,Heart 和 Student、Teacher 都是一种 “强” 关联,人不能摆脱心脏而存在,即组合关系,而 Student 和 Class、School 是一种 “弱” 关联,脱离了学校、班级,学生还能属于社会或其他团体,即聚合关系。

合成复用原则总结:

  • 新对象可以调用已有对象的功能,从而达到对象复用

5、总结

总结下这两节的内容,我们一共介绍了 7 种设计原则,它们分别为开闭原则、单一职责原则、里氏替换原则、依赖倒置原则、接口隔离原则和本节所介绍的合成复用原则。

各种原则要求的侧重点不同,总地来说:

1、开闭原则是核心,对拓展开放对修改关闭是软件设计、后期拓展的基石;
2、单一职责原则就要求我们设计接口,制定模块功能时保持模块或者接口功能单一,接口设计或功能设计尽量保持原子性,修改一处不能影响全局或其它模块;
3、里氏替换原则和依赖倒置原则,按照作者的理解,这俩原则总的是要求我们要面向接口、面向抽象编程,设计程序的时候尽可能使用基类或者接口进行对象的定义或引用,而不是具体的实现,否则实现一旦有变更,上层调用者就必须做出对应变更,这样一来,整个模块可能都需要重新调整,非常不利于后期拓展。
图片描述
4、接口隔离原则具体应用到程序中,比如我们在传统 mvc 开发时,service 层调用 dao 层一般会使用接口进行调用,各层之间尽量面向接口通信,其实也是一种降低模块耦合的方法;
5、迪米特法则的初衷也是为了降低模块耦合,代码示例中我们引入了类似 “中间人” 的概念,上层模块不直接调用下层模块,而是引入第三方进行代办,这也是为了降低模块的耦合度;
6、合成复用原则一节,我们介绍了聚合、组合的概念,聚合是一种弱关联,而组合是一种强关联,表现在 UML 类图上的话聚合是使用空心四边形加箭头表示,而组合是使用实心四边形加箭头表示,合成复用原则总的就是要求我们尽量利用好已有对象,从而达到功能复用,具体是聚合还是组合,还是一般关联,就要看具体情况再定了。

这 7 种设计原则是软件设计必须尽量遵循的原则。

}
立即订阅 ¥ 68.00

你正在阅读课程试读内容,订阅后解锁课程全部内容

千学不如一看,千看不如一练

手机
阅读

扫一扫 手机阅读

设计模式深度解析34讲
立即订阅 ¥ 68.00

举报

0/150
提交
取消