终于有人将23种设计模式与七大设计原则整理明白了!!!

10 篇文章 1 订阅

👨‍🎓作者:Java学术趴

🏦仓库:GithubGitee

✏️博客:CSDN掘金InfoQ云+社区

💌公众号:Java学术趴

🚫特别声明:原创不易,未经授权不得转载或抄袭,如需转载可联系小编授权。

🙏版权声明:文章里的部分文字或者图片来自于互联网以及百度百科,如有侵权请尽快联系小编。微信搜索公众号Java学术趴联系小编。

☠️每日毒鸡汤:朝圣的路上我们坚持自己的道。

👋大家好!我是你们的老朋友Java学术趴,最近花了差不多两周的时间研究了一下23种设计模式以及七大设计原则,我怎么可能会偷偷的学,肯定给大家整了出一篇干货笔记,这是必须的。整了了差不多20000字的干货笔记,字数可能会有点多,但是全是真真的干货呦!大家感觉有帮助的话,欢迎点赞、关注、收藏、评论呦,感谢大家支持。话不多说,直接进入主题,给大家详细介绍设计模式。

23种设计模式

1.设计模式概念

1.1 什么地方可以用到设计模式

面向对象(OO)=>功能模块[设计模式+算法(数据结构)]=>框架[使用多种设计模式]=>架构[服务器集群]

1.2 使用设计模式的好处

  • 使用设计模式,软件具有很好的可扩展性(可以增加新的功能)
  • 使用开发模式,具有很好的维护性(可读性、规范性)

1.3 设计模式的目的

  • 设计模式是为了让程序,具有更好的代码重复性、可读性(编程规范性)、可扩展性(可维护性)、可靠性、是程序呈现高内聚,低耦合的特征。(模块内部逻辑关系非常紧密,模块与模块之间的关系非常的松散)
  • 分享金句:“懂了设计模式,你就懂了面向对象分析和面向对象设计(OOA/OOD)的精要”。
  • C++老手与C++新手的区别就是,前者手背上有很多的伤疤。

2.设计模式的七大原则

  • 设计模式原则,其实就是程序员在编译时,应当遵守的原则,也就是各种设计模式的基础(即:设计模式为什么这样设计的依据)

设计模式常用的七大原则:

  • 单一职责原则
  • 接口隔离原则
  • 依赖倒转(倒置)原则
  • 里氏替换原则
  • 开闭原则
  • 迪米特原则
  • 合成复用原则(在一些地方不写这个原则)

2.1 单一职责原则

  • 对于类来说,即一个类应该只负责一项职责。如果A类负责两个不同的职责:职责1、职责2。当职责1发生变化而改变A时,可能会对职责2造成影响使职责2运行错误,所以需要将类A的粒度分解为A1、A2。
  • 如果再类中没有满足单一职责原则,在一个类的方法中遵守单一职责原则也是可以的(交通工具)
  • 标准的单一职责原则,是在类的级别上进行拆分,而不是方法级别。
  • 通常情况下,我们要遵守单一职责原则,只有当逻辑足够简单,才可以在代码级别违反单一职责原则;只有类中的方法数量足够少,可以在方法级别保持单一职责原则。
  • 优秀的代码中使用类来区分多个分支,而不使用 if…else if()…else(耦合度高)

2.2 接口隔离原则

  • 客户端不应该依赖它不需要接口,即一个类对另一个类的依赖应该建立在最小的接口上。
  • 处理方式:将接口Interface拆分为独立的几个接口,类A与类C分别于他们需要的接口建立依赖关系。这就是使用的接口隔离原则。

没有使用接口隔离原则时的实现类图:(此时A、C要实现接口里的所有方法)

使用接口隔离原则时的实现类图:(此时将接口进行了拆分,A此时只需要实现它要使用的方法对应的接口即可,而不用将接口中的方法全部实现)

2.3 依赖倒转(倒置)原则

  • 在Java中,抽象是指接口或者抽象类,细节是指具体的实现类。
  • 高层模块不应该依赖低层模块,二者都应该依赖其抽象(接口、抽象类)。
  • 抽象类不应该依赖细节,细节应该依赖抽象类。
  • 依赖倒倒转(倒置)的中心思想是面向接口编程。

依赖倒转原则是基于这样的设计理念:

  • 相对于细节的多变性,抽象的东西要稳定的多。以抽象为基础搭建的架构比细节为基础的架构要稳定的多。
  • 使用接口或者抽象类的目的是制定好规范,而不涉及任何具体的操作,把展示细节的任务交给他们的实现类去完成。

注意:在一个类文件中可以声明其他类、接口,只是这些都不能使用public修饰。但是声明的这些类和方法还是可以被其他的类继承或者实现的。

依赖关系传递的三种方式

  • 接口传递
  • 构造方法传递
  • setter方式传递

依赖原则要注意的地方

  • 底层模块尽量都要有抽象类和接口,或者两者都有,程序稳定性更好。
  • 变量的声明类型尽量是抽象类和接口,这样我们的变量引用个实际对象间,就曾在一个缓冲层,利于程序的扩展和优化。(就比如你和对象吵架,你先找丈母娘来劝说对象,而不是与对象直接沟通)
  • 继承时遵循里氏替换原则。

2.4 里氏替换原则

  • 使用继承的时候,父类会对子类进行约束。并且如果父类中的方法发生改变的时候,可能会对所有的子类造成影响。

里氏替换原则

  • 里氏替换原则是在1988年麻省理工学院的一个姓李的女士提出的。
  • 所有引用基类的地方必须先是透明的使用其子类的对象。
  • 在继承中,遵循里氏替换原则,在子类中尽量不在重写父类的方法。
  • 里氏替换原则告诉我们,继承实际上让两个类耦合性增强了,在适当的情况下,可以通过聚合、组合、依赖来解决问题。

解决问题的办法

  • 原来的父类和子类都继承一个更通俗的基类,原有的继承关系去掉,采用依赖、聚合、组合等关系替代。

2.5 开闭原则(ocp原则)

  • 开闭原则是编程中最基础、最重要的设计原则。
  • 一个软件的实体如类、模块和函数应该对扩展开放(针对提供方),对修改关闭(对使用者)。
  • 用抽象构建架构,用实现扩展细节。
  • 当软件需要变化时,尽量通过扩展软件实体的行为来实现变化,而不是通过修改已有的代码来实现。
  • 编程中遵循其他原则,以及使用设计模式的的目的就是遵循开闭原则。

2.6 迪米特法则

  • 一个对象应该对其他对象保持最少的了解。
  • 类与类关系越密切,耦合度越大。
  • 迪米特法则又叫最少知道原则,即一个类对自己依赖的类知道的越少越好。也就是说,对于依赖的类不管多么的复杂,都尽量将逻辑封装在类的内部。对外除了了提供public 方法,不对外泄露任何信息。
  • 迪米特法则还有个人更简单的定义:只与直接的朋友通信。
  • 直接的朋友: 每个对象都会与其他对象有耦合关系,只要两个对象之间有耦合关系,我们就说这两个对象之间是朋友关系。耦合的方式有很多,依赖、关联、组合、聚合等。其中 ,我们称出现在成员变量、方法参数、方法的返回值中的类为直接的朋友,而出现在局部变量中的类不是直接的朋友。也就是说,陌生的类不要以局部变量的形式出现在类的内部。

迪米特法则

  • 核心:降低类之间的耦合
  • 注意:由于每个类都减少了不必要的依赖,因此迪米特法则只是要求降低类间(对象间)耦合关系,并不是要求完全没有依赖关系。

2.7 合成复用原则

  • 基本介绍:尽量使用合成/聚合的方式,而不是使用继承。(依赖、聚合、组合)

image-20210915211256083

  • 找出应用中可能需要变化之处,把他们独立出来,不要和那些不需要变化的代码混在一起。
  • 针对接口编程,而不是针对实现编程。
  • 为了交互对象间的松耦合设计而努力

4.设计模式

4.1 设计模式概念和分类

  • 概念:设计模式的本质提高软件的维护性、通用性和扩展性,并降低软件的复杂度。
  • 《设计模式》是经典的书,作者俗称“四人组 GOF”。
  • 设计模式并不是局限于某种语言的,Java、C++、PHP都有设计模式。

4.2 设计模式的分类

  • 创建型模式:单列模式、抽象工厂模式、原型模式、建造者模式、工厂模式。
  • 结构型模式:适配器模式、桥接模式、装饰模式、组合模式、外观模式、享元模式、代理模式。
  • 行为型模式:模板方法模式、命令模式、访问者模式、迭代器模式、观察者模式、中介者模式、备忘录模式、解释器模式(Interperter模式)、状态模式、策略模式、职责链模式(责任链模式)。

注意:不同书籍对哦分类和名称略有差别。

5. 单例模式

  • 所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法(静态方法)。

image-20210923194636842

image-20210923200228383

  • 以上是使用单例模式实现的步骤。

5.1 单例(静态常量饿汉式)推荐使用

  • 之所以叫饿汉式,不论用不用这个类的对象,只要加载类的时候就会创建出来这个类的一个对象。
  • 饿汉式是线程安全的。
  • 使用饿汉式对象在类加载的时候就会被创建。
  • 使用类名.静态方法 获取到类的唯一对象。

image-20210923200325088

  • 这种单例模式可能会造成内存的浪费,因为这个对象在类加载的时候就创建出来了,如果在主方法中没有用到这个对象,就相当于白创建了这个对象,此时会造成内存的浪费。

5.2 单列(静态代码块饿汉式)推荐使用

  • 这种写法和上边的一样,可能会造成内存浪费。

5.3 单例(线程不安全懒汉式)

  • 之所以叫懒加载是因为只有用到的时候才会加载,不用到的时候不会去加载这个对象。
  • 使用懒汉式可以解决内存浪费问题,只有调用getInstance() 方法的时候才会创建对象,并且在第二次调用方法的时候,不会在从新创建一个新的对象,而是返回第一次创建的对象,保证单例模式。

  • 以上编写的代码,虽然起到了懒汉式的作用,但是只能在单线程之下使用
  • 如果在多线程下,一个线程刚刚进入到 if(instance == null) 判断语句块,还没来得及往下执行,也就是还没有创建出一个对象,另一个线程也通过了这个判断语句,这会便会产生多个实例对象。所以在多线程下是不安全的。
  • 在实际开发中,不要使用这种方式。

5.4 单例(懒汉式:线程安全,同步方法)

  • 将创建对象的方法使用 synchronized关键字来修饰,这样就可以保证线程安全,此时多个线程调用getInstance()方法的时候需要排队,等待上一线程结束才可以进行下一个线程的调用,因为此时上一个线程已创建出一个对象,此时就不会在创建出一个新的对象。不仅保证了线程安全,还可以实现单例模式。

  • 以上这种方式实现的效率太低,因为每次调用方法的时候都需要排队。

5.5 单例(懒汉式:线程安全,同步代码块)

  • 此时将 synchronized 写到 if() 条件判断中,只要多个线程都进入if()判断中,一定是线程不安全的,在实际开发种不可以使用这种方式。

image-20210923205201920

5.6 双重检查(线程安全,效率高,懒汉式模式)推荐使用

  • 在实际开发中,推荐使用这种方式。

5.7 单例(静态内部类)推荐使用

  • 推荐使用。
  • JVM在装载类的时候是线程安全的。
  • 可以保证线程安全、实现了懒加载、保证了效率。
  • 这里保证只创建一个实例对象,使用的机制是静态内部类只会加载一次,所以只执行一次对象的创建。
  • 这里保证懒加载是因为在加载 Singleton 类的时候不会创建对象,只有调用 getInstance() 方法的时候会使用静态内部类来创建对象。

5.8 单例(枚举)推荐使用

  • 在JDK的RunTime的源码中使用到了单例模式。
  • 单例模式的特点之一:不是new出来的,而是使用方法调用出来的。

5.9 JDK中源码分析

  • 在 Java.lang.Runtime 中使用到了单例模式。

6.工厂模式

6.1 简单工厂模式(静态工厂模式)

  • 简单工厂模式属于创建型模式,是工厂模式的一种。简单工厂模式是由一个工厂对象决定创建出哪一种产品类型的实例。 简单工厂模式是工厂模式家族中最简单实用的模式。
  • 简单工厂模式:定义一个创建对象的类,由这个类来 来封装实例化对象的行为(代码)
  • 在软件开发中,当我们会用到大量的创建某种、某类或者某批对象时,就会使用到工厂模式。

实现一个需求:客户可以点任意口味的披萨,奶酪披萨、胡椒等等。

实现原理:

  • 我们创建一个 SimpleFactory 工厂类,这个类负责创建出实例对象,用户给这个工厂传递需求(比如需要创建的对象),这个工厂类会通过自己类中封装的代码(行为)来创建出对应的实例对象

  • 以下就是这个简单工厂类,将这个工厂使用聚合或者组合的方式为每一个小店铺添加。这样的话不需要改变小店铺中的代码,只需要改变工厂类中的行为即可。

image-20210927102547268

  • 简单工厂模式要比静态工厂模式灵活,简单工厂模式可以任意的改变不同对象的不同行为,而静态工厂模式中行为是一样的,每个对象使用的都是这几个行为。

6.2 工厂方法模式

  • 工厂方法模式设计方案: 将披萨项目的实例化功能抽象成方法,在不同的口味点餐子类中具体实现。
  • 工厂方法模式:定义了一个创建对象的抽象方法,由子类决定要实例化的类。工厂方法模式将对象的实例化推迟到子类。

实现原理

  1. 将OrderPizza类声明为抽象类。BJOrderPizza和LDOrderPizza定义为实现这个抽象类的子类,在这个子类中文成披萨的创建。

image-20210927111646437

  1. 定义不同地区不同类型的披萨。(定义四个类,按照以下格式依此类推)

  2. 以下就是创建的这个抽象的父类,这个类来声明创建披萨对象的方法,使用其子类具体实现这个工厂类。

  • 让子类继承这个抽象工厂类,在这个子类中创建出关于北京不同的披萨类型。

image-20210927112747938

  • 实现顾客选定披萨

6.3 抽象工厂模式

  • 抽象工厂模式:定义了一个interface用于创建相关或者有依赖关系的对象簇,而无需指明具体的类。
  • 抽象工厂类可以将 简单工厂模式工厂方法模式进行整合。
  • 从设计层面看,抽象工厂模式就是对简单工厂模式的改进(或者称为进一步的抽象)
  • 将工厂抽象为两层,AbsFactory(抽象工厂)和具体实现的工厂子类。程序员可以根据创建对象类型使用对应的工厂子类。这样讲单个的加单工厂类变成了工厂簇。更利于代码的维护和扩展。

  • 以上的四个披萨类和上边的简单工厂是一样的。
  • 以下是抽象工厂类。

image-20210927150611135

  • 以下是两个工厂子类,分别实现不同地区的披萨。

  • 以下是一个实现选择披萨的类。

  • 以下是最终实现用户选择披萨的类。

image-20210927152755380

工厂模式小结:

  • 工厂模式的意义:将实例化对象的代码提取出来,放到一个类中统一管理和维护,达到和主项目的依赖关系解耦。从而提高项目的扩展性和维护性。
  • 三种工厂模式(简单工厂模式、工厂方法模式、抽象工厂模式)

工厂模式使用了设计模式的依赖抽象原则:

  • 创建对象实例时,不要直接new类,而是把这个new类的动作放在一个工厂的方法中,并返回。有的书上说,变量不要直接持有具体类的引用。
  • 不要让类继承具体类,而是继承抽象类或者是实现interface(接口)。
  • 不要覆盖基类中已实现的方法。

6.4 JDK中源码分析

  • Java中的 Calendar类 使用到了工厂模式

7. 原型模式

7.1 原型模式(克隆羊)

image-20210927165837695

  • 原型模式是指:用原型实例指定创建对象的种类,并且通过拷贝这些原型,创建新的对象。
  • 原型模式是一种创建型设计模式,允许一个对象在创建另一个可定制的对象,无需知道如何创建的细节。
  • 工作原理是:通过将一个原型对象传递给那个要发动创建的对象,这个要发动创建的对象通过请求原型对象拷贝他们自己来实施创建,即 对象.clone()

7.2 原型模式(浅拷贝)

  • 深拷贝解决的问题是在克隆对象的时候应该如何处理。
  • 使用浅拷贝方法拷贝的对象,并不会新克隆出一个对象,克隆出来的对象和原始对象指向同一个地址,也就是说克隆出来的对象和被克隆的对象是同一个对象。
  • 上边的克隆羊就是属于浅拷贝。(不会创建出新的对象)

image-20210927204350497

7.3 原型模式(深拷贝)

  • 深拷贝的核心就是 克隆出来的对象是一个全新的对象,有自己的内存空间。

image-20210927205126355

7.4 JDK中源码分析

  • Spring中原型bean的创建,就是使用原型模式的应用。

8.建造者模式

建造者模式介绍

  • 建造者模式又叫生成器模式,是一种对象构建模式。它可以将复杂对象的建造过程抽象出来(抽象类别),使这个抽象过程的不同实现方法可以构造出不同表现(属性)的对象。
  • 建造者模式 是一步一步创建一个复杂的对象,它允许用户只通过指定复杂对象的类型个内容就就可以构建他们,用户不需要知道内部的具体构建细节。

盖房项目需求

  • 需要建房子:这一过程为打桩、砌墙、封顶。
  • 房子有各种各样的类型:普通房、别墅、高楼大厦,各种房子的建造过程虽然一样,但是要求不一样。

使用普通模式实现盖房子需求

我们可以采取继承的方式来构建不同的房子。

  • 优点:比较好理解,简单易于操作。
  • 缺点:这种程序结构,设计简单,没有设计缓存层对象,程序的扩展和维护不好,也就是说,这种设计方案,把产品(即:房子)和创建产品的过程(即:建房子流程)封装在一起,耦合度增强了。
  • 解决方案:将产品与产品建造的过程解耦==》建造者模式。

使用构造者模式实现盖房子的需求

  • 以下是盖房子时是用建造者模式的类图

建造者模式

  • 客户端(使用程序)不必知道产品内部组件的细节,将产品本身与产品的创建过程解耦,使得相同的创建过程可以创建不同的产品对象。
  • 每一个具体的建造者都相对独立,而与其他的具体建造者无关,因此可以很方方便地替换具体建造者或增加具体建造者,用户使用不同的具体建造者即可得到不同的产品对象
  • 可以更加精细的控制产品的创建过程。 将复杂产品的创建步骤分解在不同方法中,使得创建过程更加清晰,也更加方便使用程序来控制床架过程。
  • 增加新的具体的建造者无须修改原有类库的代码,指挥者类针对抽象建造者编程,系统扩展方便,符合 开闭原则
  • 建造者模式所创建的产品一般具有较多的共性,其组成部分相似(比如盖房子),如果产品之间的差异性很大,则不适合使用建造者模式,因此其使用范围有一定的限制 。
  • 如果产品的内部变化复杂,可能会导致需要定义很多具体建造者类来实现这种变化,导致系统变得很庞大,因此在这种情况下,需考虑是否选择建造者模式。

抽象工厂模式VS建造者模式

  • 抽象工厂模式实现对产品家族的创建,一个产品家族是这样的一系列产品;具有不同分类维度的产品组合,采用抽象工厂模式不需要关心构建过程,只关心什么产品由什么工厂生产即可。而建造者模式则是要求按照指定的蓝图建造产品,它的主要目的是通过组装零配件而产生一个新产品。

8.1 JDK中源码分析

  • Java.lang.StringBuilder中使用到建造者模式

9. 适配器模式

9.1 适配器介绍

  • 适配器模式(包装器):将某个类的接口转换成客户端期望的另一个接口表示,主要目的是兼容性,让原本因接口因接口不匹配不能在一起工作的两个类可以协同工作。
  • 适配器模式属于结构性模式。
  • 主要分为三类:类适配器模式、对象适配器模式、接口适配器模式。

9.2 工作原理

  • 适配器模式:将一个类的接口转换成另一种接口,让 原本接口不兼容的类可以兼容。
  • 从用户的角度看不到被适配者。
  • 用户调用适配器转换出来的目标接口方法,适配器再调用被适配者的相关接口方法。
  • 用户收到反馈结果,感觉只是和目标接口交互,如图:

9.3 类适配器

类适配器模式介绍

基本介绍:Adapter类,通过继承src类,实现dst类接口,完成src–>dst的适配。

类适配器模式应用案列

以生活中充电器的例子来讲解,充电器本身相当于Adapter,220v交流电相当于src(即被适配者)。我们的目的是dst(即:目标)是5v直流电。

image-20210928111026323

这里VoltagAdapter是适配器类,这个类继承了220v的这个被适配的类,实现了5v的这个接口,Phone是手机类,这个类依赖5v的这个接口其实就是依赖的VoltageAdapter这个适配器类。Clinet是用户类,这个用户依赖手机类和这个适配器类。

  • 以上代码实现方式见 adapter类 包

使用类适配器的缺点

  • Java是单继承机制,所以类适配器需要继承src类这一点算是一个缺点,因为这要求dst必须是接口,有一定的局限性。
  • src类的方法在Adapter中都会暴露出来,也增加了使用的成本。

使用类适配器的优点

  • 由于其继承了src类,所以它可以根据需求重写src类的方法,是得Adapter的灵活性增强了。

9.4 对象适配器

对象适配器模式介绍

  • 基本介绍:和类适配器模式差不错,只是将Adapter类作为修改,不是继承src类,而是持有src类的实例,以解决兼容性的问题。即:持有src类,实现dst类接口,完成src–>dst的适配。
  • 根据”合成复用原则”,在系统中尽量使用关联关系来代替继承关系。
  • 对象适配器模式是适配器模式中常用的一种

image-20210928174226595

  • 对象适配器和类适配器其实算是一种思想,只不过实现方式不同。根据合成复用原则,使用组合替代继承,所以它解决了类适配器必须继承src的局限性问题,也不再要求dst必须是接口。
  • 使用成本更低,更灵活。

9.5 接口适配器模式

接口适配器模式介绍

  • 一些书籍称为:适配者模式或者缺省适配器模式。
  • 当不需要全部实现接口提供的方法时,可先设计一个抽象类实现接口,并为该接口中的每个方法提供一个默认实现(空方法),那么该抽象类的子类可以有选择的覆盖重写父类的某些方法来实现需求。
  • 适用于一个接口不想使用其所有方法的情况。

image-20210928215344532

  • 一下代码是使用的匿名内部类来实现的。因为AbsAdapter 是一个抽象类,不可以直接new出来,需要实现具体的方法。

image-20210928220613511

对适配器的总结

  • 适配器的三种命名方式,是根据src(被适配者)是以怎样的形式给到Adapter(在Adapter里的形式,Adapter是适配器类)类命名的。

    类适配器:以类给到,在Adapter里,就是将src当作类。(继承)。

    对象适配器:以对象给到,在Adapter里,将src作为一个对象。(聚合、组合)。

    接口适配器:以接口给到,在Adapter里,将src作为一个接口。(实现)

  • Adapter模式最大的作用还是将原本不兼容的接口结合在一起工作。

  • 实际开发中,实现起来并不拘泥这三种经典的形式。

9.6 JDK中源码分析

  • SpringMVC中的HandlerAdapter,就使用了适配器模式

10.桥接模式

10.1 使用普通模式实现问题

问题引入(手机操作问题)

使用传统的方式解决这个问题的类图

使用传统方式解决问题的缺点

  • 扩展性问题(类爆炸),如果我们在增加手机的样式(旋转式),就需要增加各个品牌手机的类,同样如果我们增加一个手机品牌,也需要在各个手机样式类下增加。
  • 违反了单一职责原则,当我们增加手机样式时,要同时增加所有品牌的手机,这样增加了代码维护成本。

10.2 桥接模式介绍

  • 桥接模式是指:将实现与抽象放在两个不同的类层次中,是两个层次可以独立改变。
  • 桥接模式是一种结构型设计模式。
  • 桥接模式基于 类的最小设计原则,通过使用封装、聚合及继承等行为让不同的类承担不同的职责。它的主要特点是把抽象与行为实现分离开来,从而可以保持各个部分的独立性以及应对他们的功能扩展。

  • 以上 Implementor 是一个接口,Abstraction是一个抽象类,剩下的类都是具体的实现类。

10.3 使用桥接模式实现问题

桥接模式实习的类图

桥接模式的总结

  • 实现了抽象和实现部分的分离,从而极大的提供了系统的灵活性,让抽象部分和实现部分分别独立出来,这有助于系统进行分层设计,从而产生更好的结构化系统。
  • 桥接模式替代多层继承方案,可以减少子类的个数,降低系统的管理和维护成本。
  • 桥接模式由于聚合关联关系建立在抽象层,要求开发者针对抽象进行设计和编程。
  • 桥接模式要求正确的识别出系统中两个独立变化的维度,因此其使用范围有一定的局限性,即需要有这样的应用场景。

桥接模式的使用场景

image-20210929112858850

10.4 JDK中源码分析

  • JDBC的Driver接口中,使用到了桥接模式。

11. 装饰者模式

11.1 使用普通模式实现问题

问题引入(咖啡馆)

使用传统方式解决问题类图

  • 问题在于这种写法会产生类爆炸。

image-20210929145139813

11.2 装饰者模式介绍

  • 装饰者模式:动态的将新的功能附加到对象上。在对象功能扩展方面,它比继承更有弹性,装饰者模式也体现了开闭原则(OCP)

装饰者模式的原理

11.3 使用装饰者模式解决星巴克问题

使用装饰者模式解决上述咖啡问题的方案图

装饰者模式下的订单:2分巧克力+1份牛奶的LongBlack(单品咖啡) ,我们生活着的思想是将配料加到咖啡中,但是使用装饰者模式的思想是:将单品咖啡加入到配料中。

image-20210929151909305

11.4 JDK中源码分析

  • Java中的IO流中的 FilterInputStream就是一个装饰者

12. 组合模式

12.1 使用普通模式是实现问题

问题引入(学校院系展示需求)

使用传统解决问题的类图使用组合的方式,学校中包含学院,学院中包含系。

使用传统方案的问题分析

  • 将学院看做是学校的子类,系是学院的子类,这样实际上是站在组织大小来进行分层次的。
  • 实际上我们的要求是:在一个页面中展示出一个学校的院系组成,一个学校有多少个学院,一个学院有多少个系,因此这种解决方案不能很好实现管理的操作。比如对学院、系得添加、删除和遍历等。
  • 解决方案:把学校、学院、系都看做是组织结构,他们之间没有继承得关系,而是一个树形结构,可以更好得实现管理操作 ==> 组合模式。

12.2 组合模式简介

  • 组合模式,又叫部分整体模式,它创建了对象组的树形结构,将对象组合成树状结构以表示”整体—部分”的层次关系。
  • 组合模式依据树形结构来组合对象,用来表示部分以及整体层次。
  • 这种类型的设计模式属于结构型设计模式。
  • 组合模式使得用户对单个对象和组合对象的访问具有一致性,即:组合能让客户以一致的方式处理个别对象以及组合对象。

组合模式原理类图

image-20210929155318828

12.3 使用组合模式解决问题

使用组模式解决文艺的类图这里的 OrganizationComponent 可以是接口、抽象类、普通类。

image-20210929164542987

  • 简化客户端操作。
  • 具有较强的扩展性。
  • 方便创建出复杂的层次结构。
  • 需要遍历组织机构,或者处理的对象具有树形结构时,非常适合使用组合组合模式。
  • 需要较高的抽象性。如果节点和叶子有很多差异性的话,比如很多方法和属性都不一样。不适合使用组合模式。

12.4 JDK中源码分析

  • Java中的集合类 HashMap就是使用了组合模式

13.外观模式

13.1 使用普通模式实现问题

问题引入(影院项目管理)

image-20210930094011641

传统方式解决问腿的原理图*

13.2 外观模式介绍

外观模式概念

  • 外观模式,也叫过程模式:外观模式为子系统中的一组接口提供一个一致的界面,此模式定义了一个高层接口,这个接口使得这一子系统更加的容易使用。
  • 外观模式通过定义一个一致的接口,用以屏蔽内部子系统的细节,是得调用端只需跟这个接口发生调用,而无需关系这个子系统的内部细节。

外观模式类图

外观模式角色分析

  • 外观类(Facade):为调用端提供统一的调用接口,外观类知道哪些子系统负责处理请求, 从而将调用端的请求代理给适当子系统对象。
  • 调用者(Client):外观接口的调用者。
  • 子系统的集合:指模块或者子系统,处理Facade对象指派的任务,他是功能的实际提供者。

外观模式的原理

12.3 外观模式实现影院管理

实现类图

12.4 外观模式注意事项

  • 外观模式对外屏蔽了子系统的细节,因此外观模式降低了客户端对子系统使用的复杂性。
  • 外观模式对客户端与子系统分耦合关系,让子系统内部的模块更容易的维护和扩展。
  • 通过合理的使用外观模式,可以帮助我们更好的规划访问的层次。
  • 当系统需要进行分层设计时,可以考虑使用Facade模式。
  • 可以使用Facade来提高接口分复用性。
  • 不能过多或者不合理的使用外观模式,判断是使用外观模式好,还是使用直接调用的模式好,要让系统有层次,利于维护的目的。

12.5 JDK中源码分析

  • MyBatis中的Configuration 去创建MetaObject 对象使用到了外观模式

13.享元模式

13.1 使用传统的方式实现问题

问题引入(网站外包问题)

image-20210930142304823

传统方式实现问题原理

image-20210930142453775

传统方法问题分析

13.2 享元模式介绍

基本介绍

  • 享元模式也叫蝇量模式 :运用共享技术有效地支持大量细粒度的对象。
  • 常用于系统底层开发,解决系统的性能问题。像数据库连接池,里面都是创建好的连接对象,在这些连接对象中有我们需要的则直接拿来使用,避免重新创建,如果没有我们需要的,则创建一个。
  • 享元模式能够解决 重复对象的内存浪费的问题,当系统中有大量相似对象,需要缓冲池时。不需总是创建新对象,可以从缓冲池里拿。这样可以降低系统内存,同时提高效率。
  • 享元模式 经典的应用场景就是池技术了,Sting常量池、数据库连接池、缓冲池等都是享元模式的应用,享元模式是池技术的重要实现方式。

享元模式的原理类图

  • Flyweight:是抽象的享元角色,他是产品的抽象类,同时定义出对象的外部状态和内部状态的接口或实现。
  • ConcreteFlyweight:是具体的享元角色,具体的产品类,实现抽象角色定义相关业务。
  • UnSharedConcreateFlyweight:是不可共享的角色,一般不会出现在享元工厂。
  • FlyweightFactory:享元工厂类,用于构建一个处理器(集合),同时提供从池中获取对象方法
  • Client:客户端。

13.3 内部状态和外部状态

  • 内部状态:对象共享出来的信息,存储在享元对象内部且不会随环境的改变而改变。
  • 外部状态:对象得以依赖的一个标记,是随环境的改变而改变的、不可共享的状态。

举个例子:

比如围棋、五子棋、跳棋。棋子的颜色就是内部状态棋子的坐标就是外部状态

比如代码内容相同的网页,展示出不同的形式(网页、博客、公众号) 。这里相同的代码就是内部状态,而展现代码的形式就是外部状态

13.4 使用享元模式实现问题

使用享元模式实现问题的类图

image-20210930153034663

13.4享元模式在JDK中的应用

  • 注意:在JDK1.8之后,将字符串常量池移到了堆内存中。
  • 这里的Hello是共享的数据,即使new了一个String,但是共享的是同一个Hello。

  • 在 java.lang.Integer中使用到了享元模式。使用Integer.valueOf(x),而不使用new 。此时只要x的范围在 -127~128 之间就是使用的享元模式。
  • 在Integer中存在缓冲区,这个缓冲区的范围就是 -127~128,只要不是new出来的,在这个范围内的Integer对象的地址是一样的。如果超出这个范围会new一个新的对象。

13.5 享元模式的注意事项

  • 在享元模式中,“享”代表共享,“元”代表对象。
  • 享元模式大大减少了对象的创建,降低了程序内存的占用,提高效率。
  • 使用享元模式提高了复杂度。需要分离出来内部状态与外部状态,比较繁琐。
  • 使用享元模式时,注意划分内部状态与外部状态,并且需要有一个工厂类加以控制。
  • 享元模式经典的应用场景就是:缓冲区。比如字符串常量池、数据库连接池。

14.代理模式

14.1 代理模式介绍

  • 代理模式:为一个对象提供一个替身,以控制这个对象的访问。 即通过代理对象访问目标对象。这样做的好处:可以在目标对象实现的基础之上,增强额外的功能操作,即扩展目标对象的功能。
  • 被代理的对象可以是:远程对象、创建开销大的对象或者需要安全控制的对象。
  • 代理模式有不同的形式:静态代理、动态代理(JDK代理、接口代理)和Cglib代理(可以在内存动态的创建对象,而不需要实现接口,他是属于动态代理的范畴。)

代理模式的原理类图

14.2 静态代理

  • 静态代理在使用时,需要定义接口或者父类,被代理对象(即目标对象)与代理对象一起实现相同的接口或者继承相同父类。
  • 调用的时候通过调用代理对象的方法来调用目标对象。代理对象与目标对象要实现相同的接口,然后通过调用相同的方法来调用目标对象的方法。

静态代理模式的原理类图

静态代理的优缺点

  • 优点:在不修改目标对象的功能前提下,能通过代理对象对目标功能扩展。
  • 缺点:因为代理对象需要与目标对象实现一样的接口, 所以会有很多代理类。
  • 缺点:一旦接口中增加了方法,目标对象与代理对象都要维护(即实现这个方法)

14.3 动态代理

动态代理简介

  • 代理对象不需要实现接口,但是目标对象要实现接口,否则不能动态代理。
  • 代理对象的生成,是利用的JDK的API(使用的就是反射机制),动态的在内存中构建代理对象。
  • 动态代理也叫做:JDK代理、接口代理。

JDK中生成代理对象的API

  • 代理类所在包:java.lang.reflect.Proxy
  • JDK实现动态代理只需要使用newProxyInstance方法。

动态代理的原理类图

14.4 Cglib代理

Cglib代理介绍

  • 静态代理哥JDK动态代理模式都要要求目标对象实现一个接口,但是有时候目标对象只是一个单独的对象,并没有实现任何的接口,这个时候可使用目标对新子类来实现代理。

  • Cglib代理也叫做 子类代理。他是在内存中构建出一个子类对象从而实现对目标对象功能扩展,有些书将Cglib代理归属到动态代理

  • Cglib是一个强大的高性能的代码生成包,它可以在运行期间扩展Java类与实现Java接口。它广泛的被许多的AOP的框架使用,例如:Spring AOP,实现方法拦截。

  • 在AOP编程中如何选择动态代理模式:

    1. 目标对象需要实现接口,用JDK代理。
    2. 目标对象不需要实现接口,用Cglib代理。
  • Cglib包的底层是通过使用字节码处理框架ASM来转换字节码并生成新的类

Cglib动态代理的实现步骤

注意:这个cglib包需要自己下载之后手动导入,JDK中不存在这个包

Cglib动态代理模式的原理类图

image-20211003135242655

使用方法

以下是被代理类

以下是代理对象

14.5 其他代理模式

  • 防火墙代理:内网通过代理穿透防火墙,实现对公网的访问。
  • 缓存代理:比如当请求图片文件等资源时,先到缓存代理取,如果取到资源则ok,如果取不到资源,再到公网或者数据库取,然后缓存。
  • 远程代理远程对象的本地代表,通过它可以 把远程对象当本地对象来调用。远程代理通过网络和正真的远程对象沟通信息。

  • 同步代理:主要使用在多线程编程中,完成多线程间同步工作。

15.模板方法模式

15.1 使用传统模式解决问题

问题引入

15.2 模板方法模式介绍

模板方法模式简介

  • 模板方法模式又叫 模板模式,在一个抽象类公开定义了执行它的方法的模板。它的子类可以按照需要重写方法实现,但调用将以抽象类中定义的方法进行。
  • 简单说,模板方法模式定义一个操作中的算法的骨架,而将一些步骤延迟到子类中,使得子类可以不改变一个算法的结构,就可以重定义该算法的某些特定的步骤。
  • 这种类型的设计模式属于行为型模式。

模板方法的原理类图

模板方法模式的角色及职务的分析

  • AbstractClass(抽象类):类中实现了模板方法(template),定义了算法的骨架,具体子类需要去实现其他的抽象方法operation2,3,4.
  • ConcreteClass 实现抽象方法operation2,3,4,以完成算法中特定子类的步骤。

15.3 使用模板方法模式实现问题

使用模板实现豆浆问题的类图

核心代码

15.4 模板模式中的钩子方法

钩子方法简介

  • 在模板方法模式的父类中,我们可以定义一个方法,这个方法默认不做任何的事情,子类可以根据情况看要不要覆盖它,该方法称为”钩子“。
  • 比如我们此时需要纯豆浆,不添加任何的配料,使用钩子方法解决问题。

15.5 模板方法模式在JDK中的应用

  • Spring IOC容器中初始化时运用到了模板方法模式。

15.6 模板方法模式注意和细节

  • 基本思想:算法只存在于一个地方,也就是父类中,容易修改。 需要修改算法时,只要修改父类的模板方法或者已实现的某些步骤,子类会继承这些修改。
  • 实现了最大化代码复用。父类的模板方法和已实现的某些步骤会被子类继承而直接使用。
  • 既统一了算法,也提供了很大的灵活性。父类的模板方法确保了算法的结构保持不变,同时由于类提供部分步骤的实现。
  • 该模式的不足之处:每一个不同的实现都需要一个子类实现,导致类的个数增加,使得系统更加庞大
  • 一般将模板方法加上final,防止子类重写模板方法。

15.7模板方法模式的使用场景

当要完成某个过程,该过程要执行一系列步骤,这一系列的步骤基本相同,但其个别步骤实现时可能不同,通常考虑使用模板方法模式来处理。

16. 命令模式

16.1 问题引入

问题引入(智能生活)

16.2命令模式介绍

命令模式介绍

  • 命令模式:在开发中,我们经常需要向某些对象发送请求,但是并不知道请求的接收者是谁,也不知道被请求的操作是哪个,此时我们只需在程序运行时指定具体的请求接收者即可。
  • 命令模式使得请求发送者与请求接收者消除彼此之间的耦合,让对象之间的调用关系更加灵活,实现解耦。
  • 在命令模式中,会将一个请求封装为一个对象,以便使用不同参数来表示不同的请求(即命令),同时命令模式也支持撤销的操作

命令模式的原理类图

命令模式的角色及职务的分析

Invoker:是调用者角色。

Command:是命令角色,需要执行的所有命令都在这里,可以是接口或者抽象类。

Receiver:接收者角色,知道如何实施和执行一个请求相关的操作。

ConcreteCommand:将一个接收者对象与一个动作绑定,调用接收者相应的操作,实现execute.

16.3 使用命令模式实现问题

使用命令模式解决问题的类图

注意:以上只是针对电灯做了开关,电视等其他的家电要在进行类的创建

16.4 命令模式在JDK中的应用

  • Spring框架的jdbc Template使用到了这个设计模式

16.5 命令模式的注意事项与细节

  • 将发起请求的对象与执行请求的对象解耦。
  • 容易设计一个命令队列。只要把命令对象放到列队,就可以多线程的执行命令。
  • 容易实现对请求的撤销和重做。

16.6 命令模式的使用场景

界面的一个按钮都是一条命令、模拟CMD(DOS命令)、订单的撤销/恢复、触发-反馈机制。

17.访问者模式

17.1 问题引入

问题引入(测评系统需求)

image-20211003172348870

17.2 访问者模式介绍

访问者模式简介

  • 封装一些作用于某种数据结构的各元素的操作,它可以在不改变数据结构的前提下定义作用于这些元素的新的操作。
  • 主要将数据结构与数据操作分离,解决数据结构操作耦合性问题。
  • 访问者模式的工作原理:在被访问的类里面加一个对外提供接待访问者的接口。

访问者模式的原理类图

访问者模式的角色及职务的分析

  • Visitor:是抽象访问者,为该对象结构中的ConcreteElement的每一个类声明一个visit操作。
  • ConcreteVisitor:是一个具体的访问值 实现每个由Vistor声明的操作,是每个操作实现的部分
  • ObjectStruture:能枚举它的元素,可以提供一个高层的接口,用来允许访问者访问元素。
  • Element:定义一个accept方法,接收一个访问者对象。
  • ConcreteElement:为具体元素,实现了 accept 方法。

17.3 使用访问者模式解决问题

使用访问修饰者解决问题的类图

image-20211003201948459

17.4 访问者模式之双分派

  • 所谓双分派是指不管类怎么变化,我们都能找到期望的方法运行。双分派意味着得到执行的操作取决于请求的种类和两个接收者的类型。

17.5 访问者模式的应用场景

  • 需要对一个对象结构中的对象进行很多不同操作(这些操作彼此没有关联),同时需要避免这些操作”污染“这些对象的类,可以选用访问者模式。
  • 如果一个系统有比较稳定的数据结构,又有经常变化的功能需求,那么访问者模式就是比较合适的。

18.迭代器模式

18.1 迭代器介绍

迭代器的简介

  • 迭代器属于行为型模式。
  • 如果我们的 集合元素是用不同的方式实现的。有数组、集合等或者其他的存储方式,当客户端要 遍历这些集合元素的时候就要使用多种遍历方式,而且还会暴露元素的内部结构,可以考虑使用迭代器模式解决。
  • 迭代器模式:提供一种遍历集合元素的统一接口,用一致的方法遍历集合元素,不需要知道集合对象的底层表示,即:不暴露其内部的结构。

迭代器的原理类图

18.2 迭代器模式在JDK中的应用

  • 在JDK—ArrayList集合中使用到了迭代器模式

18.3 迭代器模式的注意事项和细节

优点

  • 提供一个统一的方法遍历对象,客户不用在考虑聚合的类型,使用一种方法就可以遍历对象。
  • 隐藏了聚合的内部结构,客户端遍历集合的时候只能取到迭代器,而不会知道聚合的具体组成
  • 提供了一种设计思想,就是一个类应该只有一个引起变化的原因(叫做 单一职责原则)。
  • 当要展示一组相似对象,或者遍历一组相同对象时使用,适合使用迭代器模式。

缺点

每个聚合对象都要有一个迭代器,会生成多个迭代器。

19.观察者模式

19.1 使用普通方式解决问题

问题引入(天气预报)

image-20211004103954892

解决问题的思路

使用普通方式解决问题分析

  • 在WeatherData中,当增加一个第三方,都需要创建一个对应的第三方的公告板对象。并加入到datachange,不利于维护,也不是动态获取的。

19.2 观察者模式介绍

观察者模式原理

19.3 使用观察者模式解决问题

观察者解决问题的类图

19.4 观察者模式在JDK中的应用

  • JDK中的Observable类使用到了观察者模式。

20.中介者模式

20.1 使用普通方式解决问题

问题引入(智能家电)

20.2 中介者模式介绍

中介者模式简介

  • 中介者模式:用一个中介对象来封装一系列的对象交互。中介者使各个对象不需要显式地相互引用,从而使其耦合松散,而且可以独立的改变他们之间的交互。
  • 中介者模式属于行为型模式,使代码易于维护。
  • 比如MVC模式,C(Controller控制器)、M(Model模型)、V(View视图)的中介者,在前后端交互时起到了中间人的作用。

中介者模式的原理类图

简单理解中介者模式的类图

20.2 使用中介者模式解决问题(智能家庭)

实现类图

20.3 中介者模式注意事项与细节

  • 多个类相互耦合,会形成网状结构,使用中介者模式将网状结构分离为星型结构,进行耦合。
  • 减少类间依赖,降低了耦合,符合迪米特法则。
  • 中介者模式承担了较多的责任,一旦中介出现了问题,整个系统就会受到影响。
  • 如果设计不当,中介者对象本身会变得很复杂,这点在实际开发中,需要注意。

21.备忘录模式

21.1 使用普通方式解决问题

问题引入(游戏角色状态)

普通方案解决类图

image-20211004114422286

普通解决方案问题分析

  • 一个对象,就对应一个保存对象状态的对象,这样当我们游戏的对象很多时,不利于管理,开销较大。
  • 传统的方式是简单地做备份,new出另一个对象出来,再把需要备份的数据放到这个新的对象,但是这就暴露了对象内部的细节。

21.2 备忘录模式介绍

备忘录模式简介

  • 备忘录模式:在不破坏封装的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可以将该对象恢复到原先保存的状态。
  • 可以这样理解备忘录模式:现实生活中的备忘录是用来记录某些要去做的事情或者是记录已经达成的共同意见的事情,以防止忘记。而在软件开发中,备忘录模式有相同的含义,备忘录对象主要用来记录一个对象的某种状态,或者某些数据,当要做回退时,可以从备忘录对象里获取原来的数据进行恢复操作。
  • 备忘录模式属于行为型模式。

备忘录模式的原理类图

image-20211004125632992

备忘录模式的角色及职务的分析

  • originator:对象(需要保存状态的对象)。
  • Memento:备忘录对象,负责保存好记录,即Originator内部状态。
  • Caretaker:守护者对象,负责保存多个备忘录对象,使用集合管理,提高效率。

21.3 使用备忘录模式解决问题

备忘录模式的类图

image-20211004132933989

21.4 备忘录模式的注意事项和细节

  • 给用户提供一种可以恢复状态的机制,可以使用户能够比较方便地找回到某个历史的状态。
  • 实现了信息的封装,使得用户不需要关心状态的保存细节。
  • 如果类的成员变量过多,势必会占用比较大的资源,而且每一次保存都会消耗一定的内存。

21.5 备忘录模式的适用场景

  • 后悔药。
  • 打游戏时的存档。
  • Windows中的 ctrl+z(撤销)。
  • IE中的后退。
  • 数据库的事务管理。

为了节约内存,备忘录模式可以和原型模式配合使用

22.解释器模式

22.1 使用传统方式解决问题

问题引入(四则运算)

22.2 解释器模式介绍

解释器模式简介

  • 在编译原理中,一个算数表达式通过 词法分析器形成词法单元,而后这些词法单元再通过 语法分析器构建语法树,最终形成一颗抽象的语法分析树。这里的词法分析器语法分析器都可以看作是解释器。
  • 解释器并不是我们想象的一个简单的翻译器,解释器的内部结构非常饿复杂。可能含有多个解释器类,这些类之间相互关联。
  • 计时器模式:是指给定一个语言(表达式),定义它的文法的一种表示,并定义一个解释器,使用该解释器来解释语言中的句子(表达式)

解释器模式的原理类图

解释器模式的角色及职务的分析

22.3 使用解释器模式来实现四则表达式

解决问题:计算a+b-c 的数字

使用解释器解决为问题的类图

22.4 解释器模式在JDK中应用

  • Spring中的 SpelExpressionParse使用到了解释器模式

22.6 解释器模式的注意事项和细节

  • 当有一个语言需要解释执行,可将该语言中的句子表示为一个抽象的语法树,就可以考虑使用解释器模式,让程序具有良好的扩展性。
  • 使用解释器模式可能带来的问题:解释器模式会引起类膨胀、解释器模式采用递归调用方法,将会导致调试非常复杂、效率可能降低。

22.5 解释器的应用场景

  • 应用可以将一个需要解释执行的语言中的句子表示为一个抽象语法树。
  • 一些重复出现的问题可以用一种简单的语句来表达。
  • 一个简单语法需要解释的场景。
  • 编译器、运算表达式计算、正则表达式、机器人等。

23. 状态模式

23.1 使用传统的方式解决问题

问题引入(APP抽奖活动)

image-20211004155849504

23.2 状态模式的介绍

状态模式的简介

  • 状态模式:它主要用来解决对象在多种状态转换时,需要对外输出不同的行为的问题。状态和行为是一一对应的关系,状态之间可以相互转换。
  • 当一个对象的内在状态改变时,允许改变其行为,这个对象看起来像是改变了其类。

状态模式的原理类图

状态模式的角色及职务的分析

  • Context 类:为环境角色,用于维护State实例,这个实例定义当前状态。
  • State接口:是抽象状态角色,定义一个接口疯转与Context的一个特点接口相关行为。
  • ConcreteState类:是具体的状态角色,每一个子类实现一个与Context的一个状态相关行为。

23.3 使用状态模式解决抽奖问题

使用状态模式解决问题的类图

image-20211004162346390

23.4 状态模式的注意事项和细节

  • 代码有很强的可读性。状态模式将每个状态的行为封装到对应的一个类中。
  • 方便维护。不在需要使用if-else()语句来完成。
  • 符合”开闭原则“。容易增删状态。
  • 会产生很多的类。每一个状态都有一个对应的类,当状态过多时会产生很多的类,加大维护难度。

23.5 状态模式的应用场景

当一个事件或者对象有很多种状态,状态之间会相互转换,对不同的状态要求有不同的行为的时候,可以考虑使用状态模式。

24. 策略模式

24.1 使用普通方式解决问题

问题引入(鸭子问题)

普通方式解决问题的类图

在Duck类(抽象类或者接口)中给定所有鸭子的共同行为,让不同的鸭子继承这个Duck,不同的鸭子可能还有不同的行为,让不同的鸭子自己实现。

image-20211004165832180

使用普通方式问题分析

image-20211004170516941

24.2 策略模式介绍

策略模式简介

  • 策略模式:策略模式中,定义算法簇,分别封装起来,让他们之间可以相互替换,次模式让算法的变化独立于使用算法的客户。

  • 这算法体现了几个设计原则:

    1. 把变化的代码从不变的代码中分离出来。
    2. 针对接口编程而不是具体的类(定义了策略接口)。
    3. 多用组合/聚合,少用继承(客户通过组合方式使用策略)。

策略模式的类图

24.3 使用策略模式解决问题

思路分析与类图

策略模式:分别封装行为接口,实现算法簇,超类里放行为接口对象,在子类里具体设定行为对象。原则是:分离变化部分,封装接口,基于接口编程各种功能。此模式让行为的变化独立于算法的使用者。

24.4 策略模式在JDK中的应用

  • Arrays的Comparator就使用了策略模式

24.5 策略模式的注意事项以及细节

  • 策略模式的关键:分析项目中变化部分与不变部分。
  • 策略模式的核心思想:多用组合/聚合,少用继承;用行为类组合,而不是行为的继承。
  • 体现了“对修改关闭,对扩展开放”原则。客户端增加行为不需要修改以前的代码,只需添加一种策略(或者行为)即可,避免了使用多重转移语句(if…else())。
  • 需要注意的是:每添加一个策略就要增加一个类,当策略过多时会导致类的数目庞大。

25.职责链模式

25.1 使用普通方式解决问题

问题引入(OA系统采购审批)

普通方式实现问题的类图

普通方式实现问题分析

25.2 职责链模式介绍

职责链模式简介

  • 职责链模式又叫 责任链模式,为请求创建了一个接收者对象的链。这种模式对请求的发送者和接收者就行解耦。
  • 职责链通常每个接收者都包含另一个接收者的引用。如果一个对象不能处理该请求,那么他会把相同的请求传递给下一个接收者,依此类推。
  • 这种设计模式属于行为型模式

职责链模式的原理类图

25.3 职责链模式在JDK中的应用

  • 在SpringMVC中的HandlerExecution类使用到了职责链模式。

25.4 职责链模式的注意事项和细节

  • 讲请求和处理分开,实现解耦,提高系统的灵活性。
  • 简化了对象,使对象不需要知道链的结构。
  • 调试不方便。采用了类似递归的方式,调试时逻辑可能比较复杂。

25.5 职责链的应用场景

  • 有多个对象可以处理同一个请求,比如:多级请求、请假/加薪等审批流程、JavaWeb中Tomcat对Encoding的处理、拦截。

以上项目的源代码,点击星球🌍进行免费获取 星球(Github地址) 如果没有Github的小伙伴儿。可以搜索🔍微信公众号:Java学术趴,📭发送设计模式,免费给发给大家项目源码,代码是经过小编亲自测试🔧的,绝对可靠,免费拿去使用。

—💘看完的大佬们可以关注一下小编,会一直更新小技巧,免费分享给大家呦!!!💝–

  • 0
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值