设计模式总结

概述

软件设计模式(Software Design Pattern),又称设计模式,是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。它描述了在软件设计过程中的一些不断重复发生的问题,以及该问题的解决方案。

设计模式是软件设计中常见问题的典型解决方案。 每个模式就像一张蓝图, 你可以通过对其进行定制来解决代码中的特定设计问题。

文章目录

  • 概述
    • 设计模式目的
    • 设计模式常用的七大原则
      • 单一职责原则
      • 接口隔离原则
      • 依赖倒转(倒置)原则
      • 里氏替换原则
      • 开闭原则
      • 迪米特法则
      • 合成复用原则
      • 小结
  • UML类图
    • 类之间的关系
      • 依赖关系
      • 泛化关系
      • 实现关系
      • 关联关系
      • 聚合关系
      • 组合关系
    • IDEA与Eclipse 使用UML类图
  • 设计模式分类
    • 根据目的来分
    • 根据作用范围来分
  • 23种设计模式
    • 创建型模式
      • 单例模式
      • 原型模式
        • 模式的结构
        • 类图
        • 对象的拷贝
          • 浅拷贝
          • 深拷贝
        • 原型模式的注意事项和细节
        • 适用场景
      • 建造者模式
        • 类图
          • 模式结构
          • 优缺点
          • 代码
          • 建造者模式的注意事项和细节
      • 工厂模式
        • 简单工厂
          • 模式结构
          • 类图
          • 主要代码
          • 优缺点
          • 使用场景
        • 工厂方法
          • 模式结构
          • 类图
          • 代码实现
      • 抽象工厂模式
        • 模式结构
        • 类图
        • 模式的应用场景
        • 与工厂模式的区别
        • 与建造者模式的区别
        • 代码实现
    • 结构型模式
      • 适配器模式
      • 桥接模式
        • 模式结构
        • 类图
        • 优缺点
        • 应用场景
        • JDBC 中的桥接模式
        • 示例代码
      • 装饰者模式
        • 模式结构
        • 类图
        • IO结构中的装饰者模式
        • 优缺点
        • 应用场景
        • 示例代码
      • 组合模式
        • 模式结构
        • 类图
        • 优缺点
        • 应用场景
        • 示例代码
      • 外观模式
        • 模式结构
        • 类图
        • 优缺点
        • MyBatis中的外观模式
        • 应用场景
        • 示例代码
      • 享元模式
        • 模式结构
        • 类图
        • 优缺点
        • 应用场景
        • 示例代码
      • 代理模式
    • 行为型模式
      • 模版模式
        • 模式结构
        • 类图
        • Spring框架中的模板模式
        • 优缺点
        • 应用场景
        • 示例代码
      • 解释器模式
        • 模式结构
        • 类图
        • 优缺点
        • 应用场景
        • Spring框架中的解释器模式
        • 示例代码
      • 命令模式
      • 访问者模式
      • 迭代器模式
      • 观察者模式
      • 中介者模式
      • 备忘录模式
      • 状态模式
      • 策略模式
      • 职责链模式

设计模式目的

  1. 代码重用性 (即:相同功能的代码,不用多次编写)
  2. 可读性 (即:编程规范性, 便于其他程序员的阅读和理解)
  3. 可扩展性 (即:当需要增加新的功能时,非常的方便,称为可维护)
  4. 可靠性 (即:当我们增加新的功能后,对原来的功能没有影响)
  5. 使程序呈现 高内聚(模块与模块之间紧密联系), 低耦合(功能与功能之间依赖性低)的特性

设计模式常用的七大原则

  1. 单一职责原则
  2. 接口隔离原则
  3. 依赖倒转(倒置)原则
  4. 里氏替换原则
  5. 开闭原则
  6. 迪米特法则
  7. 合成复用原则

单一职责原则

对类来说的,即一个类应该只负责一项职责。

 如类A负责两个不同职责:职责1,职责2。当职责1需求变更而改变A时,可能造成职责2执行错误,所以需要将类A的粒度分解为A1,A2 
 当客户端需要该对象的某一个职责时,不得不将其他不需要的职责全都包含进来,从而造成冗余代码或代码的浪费。

单一职责原则注意事项和细节

  1. 降低类的复杂度,一个类只负责一项职责。
  2. 提高类的可读性,可维护性
  3. 降低变更引起的风险
  4. 通常情况下,我们应当遵守单一职责原则,只有逻辑足够简单,才可以在代码级违反单一职责原则; 只有类中方法数量足够少,可以在方法级别保持单一职责原则

接口隔离原则

客户端不应该依赖它不需要的接口,即一个类对另一个类的依赖应该建立在最小的接口上。
将接口Interface 拆分为独立的几个接口, 让需要依赖的类分别与他们需要的接口建立依赖关系。也就是采用接口隔离原则

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

  • 接口尽量小,但是要有限度。一个接口只服务于一个子模块或业务逻辑。
  • 为依赖接口的类定制服务。只提供调用者需要的方法,屏蔽不需要的方法。
  • 了解环境,拒绝盲从。每个项目或产品都有选定的环境因素,环境不同,接口拆分的标准就不同深入了解业务逻辑。
  • 提高内聚,减少对外交互。使接口用最少的方法去完成最多的事情。

设计模式总结_第1张图片
类A通过接口Interface1依赖类B,类C通过接口Interface1依赖类D,如果接口
Interface1对于类A和类C来说不是最小接口,
那么类B和类D必须去实现他们不需要的方法。

接口隔离原则处理:
将接口Interface1拆分为独立的几个接口,
类A和类C分别与他们需要的接口建立依赖
关系。也就是采用接口隔离原则

依赖倒转(倒置)原则

  1. 高层模块不应该依赖低层模块,二者都应该依赖其抽象(抽象类,接口)
  2. 抽象不应该依赖细节,细节应该依赖抽象
  3. 依赖倒转(倒置)的中心思想是面向接口编程
  4. 依赖倒转原则是基于这样的设计理念:相对于细节的多变性,抽象的东西要稳定多。
    以抽象为基础搭建的架构比以细节为基础的架构要稳定的多。在java中,抽象指的是接口或抽象类,细节就是具体的实现类
  5. 使用接口或抽象类的目的是制定好规范,而不涉及任何具体的操作,把展现细节的任务交给他们的实现类去完成

依赖倒置原则的主要作用:

  • 依赖倒置原则可以降低类间的耦合性。
  • 依赖倒置原则可以提高系统的稳定性。
  • 依赖倒置原则可以减少并行开发引起的风险。
  • 依赖倒置原则可以提高代码的可读性和可维护性。

依赖关系传递的三种方式

  1. 接口传递

  2. 构造方法传递

  3. setter方式传递

里氏替换原则

继承的弊端

  • 继承在给程序设计带来便利的同时,也带来了弊端。比如使用继承会给程序带来侵
  • 入性,程序的可移植性降低,增加对象间的耦合性,如果一个类被其他的类所继承,
  • 则当这个类需要修改时,必须考虑到所有的子类,并且父类修改后,所有涉及到子类的功能都有可能产生故障

里氏替换原则

  1. 子类可以实现父类的抽象方法,但不能覆盖父类的非抽象方法
  2. 子类中可以增加自己特有的方法
  3. 当子类的方法重载父类的方法时,方法的前置条件(即方法的输入参数)要比父类的方法更宽松
  4. 当子类的方法实现父类的方法时(重写/重载或实现抽象方法),方法的后置条件(即方法的的输出/返回值)要比父类的方法更严格或相等

核心
在使用继承时,子类可以扩展父类的功能,但不能改变父类原有的功能,在子类中尽量不要重写父类的方法,如果迫不得已可以通过聚合,组合,依赖来解决问题

开闭原则

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

核心:对扩展开放(对提供方),对修改关闭(对使用方)

迪米特法则

  1. 一个对象应该对其他对象保持最少的了解
  2. 类与类关系越密切,耦合度越大
  3. 迪米特法则(Demeter Principle)又叫 最少知道原则,即一个类对自己依赖的类知道的越少越好。也就是说,对于被依赖的类不管多么复杂,都尽量将逻辑封装在类的内部。对外除了提供的public方法,不对外泄露任何信息
  4. 迪米特法则还有个更简单的定义:只与直接的朋友通信

核心:一个类对自己依赖的类知道的越少越好;从依赖者的角度来说,只依赖应该依赖的对象。从被依赖者的角度说,只暴露应该暴露的方法。

直接的朋友:

每个对象都会与其他对象有耦合关系,只要两个对象之间有耦合关系,
我们就说这两个对象之间是朋友关系。耦合的方式很多,依赖,关联,组合,聚合等。其中,我们称出现成员变量,方法参数,方法返回值中的类为直接的朋友,
而出现在局部变量中的类不是直接的朋友。也就是说,陌生的类最好不要以局部变量的形式出现在类的内部。

迪米特法则注意事项和细节

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

合成复用原则

原则是尽量使用合成/聚合的方式,而不是使用继承.如果要使用继承关系,则必须严格遵循里氏替换原则。合成复用原则同里氏替换原则相辅相成的,两者都是开闭原则的具体实现规范。

设计原则核心思想

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

小结

各种原则要求的侧重点不同,归纳总结软件设计模式的七大原则
设计模式总结_第2张图片

UML类图

UML 建模的核心是模型,模型是现实的简化、真实系统的抽象。UML 提供了系统的设计蓝图。当给软件系统建模时,需要采用通用的符号语言,这种描述模型所使用的语言被称为建模语言。在 UML 中,所有的描述由事物、关系和图这些构件组成。下图完整地描述了所有构件的关系。其中类图是其最重要的部分也是最常用的图

在UML类图中

  • 抽象类或抽象方法用斜体表示
  • 如果是接口,则在类名上方加 <>
  • 字段和方法返回值的数据类型非必需
  • 静态类或静态方法加下划线

类之间的关系

依赖、泛化(继承)、实现、关联、聚合与组合

依赖关系

只要是在类中用到了对方,那么他们之间就存在依赖关系。如果没有对方,连编绎都通过不了 以下5中情况都是依赖关系

  1. 类中用到了对方
  2. 如果是类的成员属性
  3. 如果是方法的返回类型
  4. 是方法接收的参数类型
  5. 方法中使用到
    设计模式总结_第3张图片

泛化关系

泛化关系实际上就是继承关系,它是依赖关系的特例,表示一般与特殊的关系,是父类与子类之间的关系

设计模式总结_第4张图片

实现关系

实现关系实际上就是A类实现B接口,它是依赖关系的特例。在这种关系中,类实现了接口,类中的操作实现了接口中所声明的所有的抽象操作。

设计模式总结_第5张图片

关联关系

关联关系实际上就是类与类之间的联系,它是依赖关系的特例

  • 关联具有导航性:即双向关系或单向关系
  • 关系具有多重性:如“1”(表示有且仅有一个),“0…”(表示0个或者多个),
    “0,1”(表示0个或者一个),“n…m”(表示n到 m个都可以),“m…*”(表示至少m个
    设计模式总结_第6张图片

聚合关系

聚合关系表示的是整体和部分的关系,代表整体与部分可以分开。聚合关系是关联关系的特例,所以他具有关联的导航性与多重性

设计模式总结_第7张图片

组合关系

也是整体与部分的关系,但是整体与部分不可以分开。

设计模式总结_第8张图片

IDEA与Eclipse 使用UML类图

在IDEA中可以可以右键包然后选择Diagrams 选择类图,也可以使用快捷键crtl+alt+shift+u来显示类图,使用IDEA自带的需要先设计好程序后才能显示类图,即无法自己画类图,如果需要自己画类图则安装插件PlantUML Itegration

使用PlantUML Itegration 制作的建造者模式的UMl类图示例:具体使用规则参考 PlantUML Itegration插件 使用规则
设计模式总结_第9张图片
如果是Eclipse 则可以使用 UML插件(AmaterasUML) 来制作类图

注意:
类图(三种类型(组合、聚合、关联)在IntelliJ IDEA均以实线+菱形箭头+普通箭头表示,菱形箭头指向整体,普通箭头指向部分,箭头两端的数字表示实例的个数

设计模式分类

根据目的来分

创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。

结构型模式,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。

行为型模式,共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。

创建型模式:就是创建对象的模式,抽象了实例化的过程。它帮助一个系统独立于如何创建、组合和表示它的那些对象。关注的是对象的创建,创建型模式将创建对象的过程进行了抽象,也可以理解为将创建对象的过程进行了封装,作为客户程序仅仅需要去使用对象,而不再关心创建对象过程中的逻辑。

结构型模式:为解决怎样组装现有的类,设计他们的交互方式,从而达到实现一定的功能的目的。结构型模式包容了对很多问题的解决。例如:扩展性(外观、组成、代理、装饰)封装性(适配器,桥接)

行为型模式:涉及到算法和对象间职责的分配,行为模式描述了对象和类的模式,以及它们之间的通信模式,行为型模式刻划了在程序运行时难以跟踪的复杂的控制流
可分为行为类模式和行为对象模式

  • 1.行为模式使用继承机制在类间分派行为
  • 2.行为对象模式使用对象聚合来分配行为。

一些行为对象模式描述了一组对等的对象怎样相互协作以完成其中任何一个对象都无法单独完成的任务。
设计模式总结_第10张图片

根据作用范围来分

根据模式是主要用于类上还是主要用于对象上来分,这种方式可分为类模式和对象模式两种。
类模式:用于处理类与子类之间的关系,这些关系通过继承来建立,是静态的,在编译时刻便确定下来了。GoF中的工厂方法、(类)适配器、模板方法、解释器属于该模式。
对象模式:用于处理对象之间的关系,这些关系可以通过组合或聚合来实现,在运行时刻是可以变化的,更具动态性。GoF 中除了以上 4 种,其他的都是对象模式。

设计模式总结_第11张图片

23种设计模式

创建型模式

单例模式

单例模式

原型模式

原型模式(Prototype模式)是指:用原型实例指定创建对象的种类,并且通过拷贝这些原型,创建新的对象

原型模式是一种创建型设计模式,允许一个对象再创建另外一个可定制的对象,无需知道如何创建的细节

概括:将一个对象作为原型,通过对其进行复制而克隆出多个和原型类似的新实例

工作原理 :通过将一个原型对象传给那个要发动创建的对象,这个要发动创建的对象通过请求原型对象拷贝它们自己来实施创建,即对象.clone();

模式的结构

主要角色:

  • 抽象原型类:规定了具体原型对象必须实现的接口。
  • 具体原型类:实现抽象原型类的 clone() 方法,它是可被复制的对象。
  • 访问类:使用具体原型类中的 clone() 方法来复制新的对象。
类图

设计模式总结_第12张图片

对象的拷贝
浅拷贝

浅拷贝就是只拷贝了对象的基本类型值和引用值,但被复制的对象引用类型的属性仍然指向原来的对象。说白了拷贝对象仅仅拷贝了原对象属性值的一个引用,此引用仍然指向相同的地址
String 除外 ,实现Cloneable 覆写Clone 方法 这个就是浅拷贝
String的内容保存在常量池中,所以对其做修改只是改变了成员变量所引用的常量池的串,不会影响其它拷贝对象中的该成员

public class Sheep implements Cloneable {
    private String name;
    private int age;
    private String color;
    private String address = "蒙古羊";
    public Sheep friend; //是对象, 克隆是会如何处理
    public Sheep(String name, int age, String color) {
        super();
        this.name = name;
        this.age = age;
        this.color = color;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public String getColor() {
        return color;
    }
    public void setColor(String color) {
        this.color = color;
    }



    @Override
    public String toString() {
        return "Sheep [name=" + name + ", age=" + age + ", color=" + color + ", address=" + address + "]";
    }
    //克隆该实例,使用默认的clone方法来完成
    @Override
    protected Object clone() throws CloneNotSupportedException {

        return super.clone();
    }


}
深拷贝

就是对所有的属性都独立地复制一份。对于基本类型属性即是复制一份值(成员变量自会提供独立的空间),而对于引用类型的属性就是重新建立了一个一模一样的对象,引用值不再相同(不再指向同一个对象)

  • 实现Cloneable 一层一层覆写Clone 方法 也可以实现深度拷贝
    如果想实现引用数据的类型的属性也是引用类型的对象拷贝,则需要使用深度拷贝

  • 直接使用Java的clone()可以实现深拷贝,

  • 使用序列化实现深拷贝是一种好的方式,也可以考虑使用一些其它工具,如Spring的BeanUtils工具

public class Sheep implements Cloneable {
    private String name;
    private int age;
    private String color;
    private String address = "蒙古羊";
    public Sheep friend; //是对象, 克隆是会如何处理
    public Sheep(String name, int age, String color) {
        super();
        this.name = name;
        this.age = age;
        this.color = color;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public String getColor() {
        return color;
    }
    public void setColor(String color) {
        this.color = color;
    }

    public void setFriend(Sheep friend) {
        this.friend = friend;
    }

    public Sheep getFriend() {
        return friend;
    }

    @Override
    public String toString() {
        return "Sheep [name=" + name + ", age=" + age + ", color=" + color + ", address=" + address + "]";
    }
    //克隆该实例,使用默认的clone方法来完成--对象的深拷贝
    @Override
    protected Object clone() throws CloneNotSupportedException {

        Sheep clone = (Sheep)super.clone();

        if (friend != null) {
            clone.friend = (Sheep) clone.friend.clone();
        }
        else  clone.friend = (Sheep) super.clone();


        return clone;
    }


}

原型模式的注意事项和细节
  1. 创建新的对象比较复杂时,可以利用原型模式简化对象的创建过程,同时也能够提高效率
  2. 不用重新初始化对象,而是动态地获得对象运行时的状态
  3. 如果原始对象发生变化(增加或者减少属性),其它克隆对象的也会发生相应的变化,无需修改代码
  4. 在实现深克隆的时候可能需要比较复杂的代码
  5. 缺点:需要为每一个类配备一个克隆方法,这对全新的类来说不是很难,但对已有的类进行改造时,需要修改其源代码,违背了ocp原则
适用场景
  • 对象之间相同或相似,即只是个别的几个属性不同的时候。
  • 创建对象成本较大,例如初始化时间长,占用CPU太多,或者占用网络资源太多等,需要优化资源。
  • 创建一个对象需要繁琐的数据准备或访问权限等,需要提高性能或者提高安全性。
  • 系统中大量及频繁使用该类对象,且各个调用者都需要给它的属性重新赋值。

建造者模式

建造者模式(Builder Pattern )又叫生成器模式,是一种对象构建模式。它可以将复杂对象的建造过程抽象出来(抽象类别),使这个抽象过程的不同实现方法可以构造出不同表现(属性)的对象。

建造者模式是一步一步创建一个复杂的对象,它允许用户只通过指定复杂对象的类型和内容就可以构建它们,用户不需要知道内部的具体构建细节。

概括:将一个复杂对象分解成多个相对简单的部分,然后根据不同需要分别创建它们,最后构建成该复杂对象

核心:将产品和产品建造过程解耦;对对象创建进行组装,如果产品之间的差异性很大,则不适合使用建造者模式。

类图

设计模式总结_第13张图片

模式结构
  1. Product (产品角色):一个具体的产品对象。
  2. Builder (抽象建造者):创建一个Product对象的各个部件指定的接口/抽象类。
  3. ConcreteBuilder (具体建造者):实现接口,构建和装配各个部件。
  4. Director (指挥者):构建一个使用Builder接口的对象。它主要是用于创建一个复杂的对象。它主要有两个作用,一是:隔离了客户与对象的生产过程,二是:负责控制产品对象的生产过程
优缺点

优点

  • 封装性好,构建和表示分离。
  • 扩展性好,各个具体的建造者相互独立,有利于系统的解耦。
  • 客户端不必知道产品内部组成的细节,建造者可以对创建过程逐步细化,而不对其它模块产生任何影响,便于控制细节风险。

缺点

  • 产品的组成部分必须相同,这限制了其使用范围。
  • 如果产品的内部变化复杂,如果产品内部发生变化,则建造者也要同步修改,后期维护成本较大。
代码

使用建造者模式模拟构建房屋

类图:
设计模式总结_第14张图片

public abstract class HorseAbsBuilder {

    protected  Horse horse = new Horse();

    protected  abstract void  buildFoundation();
    protected  abstract void  buildWall();
    protected  abstract void  buildRoofed();


    public  void  horseBuilder(){
        buildFoundation();
        buildWall();
        buildRoofed();
    }

    public  Horse horseBuilderFinished() {

        return horse;
    }
}



public class HorseDirector {

    private HorseAbsBuilder builder;

    public  HorseDirector( ) { }

    public void setHouseBuilder(HorseAbsBuilder builder) {
        this.builder = builder;
    }

     public  Horse  horseDirector(){

         builder.horseBuilder();

         return builder.horseBuilderFinished();
     }

}


public class CommonHorseConcreteBuilder extends  HorseAbsBuilder{
    @Override
    protected void buildFoundation() {
        System.out.println("给普通房子建造地基");
    }

    @Override
    protected void buildWall() {
        System.out.println("给普通房子砌墙");
    }

    @Override
    protected void buildRoofed() {
        System.out.println("给普通房子封顶");
    }
}

public class Horse {

    private String foundation;
    private String wall;
    private String roofed;


    public String getFoundation() {
        return foundation;
    }
    public void setFoundation(String foundation) {
        this.foundation = foundation;
    }
    public String getWall() {
        return wall;
    }
    public void setWall(String wall) {
        this.wall = wall;
    }
    public String getRoofed() {
        return roofed;
    }
    public void setRoofed(String roofed) {
        this.roofed = roofed;
    }

public class BuildClient {

    public static void main(String[] args) {
        HorseAbsBuilder comHorseBuilder = new  CommonHorseConcreteBuilder();
        HorseAbsBuilder villaHorseBuilder = new villaHorseConcreteBuilder();

        HorseDirector dir = new HorseDirector( );
        dir.setHouseBuilder(comHorseBuilder);
        System.out.println("普通房子"+dir.horseDirector());
        dir.setHouseBuilder(villaHorseBuilder);

        System.out.println("别墅房子"+dir.horseDirector());

    }
}

}


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

工厂模式

定义一个创建产品对象的工厂接口,将产品对象的实际创建工作推迟到具体子工厂类当中。这满足创建型模式中所要求的“创建与使用相分离”的特点。

凡是需要生成复杂对象的地方,都可以尝试考虑使用工厂模式来代替

简单工厂

简单工厂模式是属于创建型模式,是工厂模式的一种。 简单工厂模式是由一个工厂对象决定创建出哪一 种产品类的实例。简单工厂模式是工厂模式家族中最简单实用的模式

简单工厂模式:定义了一个创建对象的类,由这个类来 封装实例化对象的行为(代码)

模式结构
  • 简单工厂(SimpleFactory):是简单工厂模式的核心,负责实现创建所有实例的内部逻辑。工厂类的创建产品类的方法可以被外界直接调用,创建所需的产品对象。
  • 抽象产品(Product):是简单工厂创建的所有对象的父类,负责描述所有实例共有的公共接口。
  • 具体产品(ConcreteProduct):是简单工厂模式的创建目标。
类图

设计模式总结_第15张图片

主要代码

使用简单工厂生产USB接口

设计模式总结_第16张图片

public class SimpleFactory {

    public static  USBInterface create(String type) {

        switch (type) {
            case "Flash" -> {

                return new Flash();
            }
            case "Printers" -> {

                return new Printers();
            }
            default -> {

                return new Flash();
            }
        }
    }
}
 public class Flash implements USBInterface {

    @Override
    public void start() {

        System.out.println("U盘开始传输数据");
    }

    @Override
    public void stop() {
        System.out.println("U盘被拔出");

    }

}

public class Printers implements USBInterface{

    @Override
    public void start() {
        System.out.println("打印机开始工作");

    }

    @Override
    public void stop() {
        System.out.println("打印机停止工作");

    }
}

public class Order {

    String orderType;

    public  Order(String orderType) {

        this.orderType = orderType;

    }

    public   void  getUSB() {

        USBInterface usbInterface = SimpleFactory.create(orderType);
        usbInterface.start();
        usbInterface.stop();
    }
}

interface USBInterface {

    void start();

    void stop();
}

public class SimpleFactoryModeTest{
    //简单工厂模式(静态工厂模式)--简单--可扩展性低,不满足 开闭原则OCP“对扩展开发,对修改关闭”原则

    public static void main(String[] args) {
        Order order = new Order("Flash");
        order.getUSB();
        Order order1 = new Order("Printers");
        order1.getUSB();
    }
}
优缺点

优点

  • 工厂类包含必要的逻辑判断,可以决定在什么时候创建哪一个产品的实例。客户端可以免除直接创建产品对象的职责,很方便的创建出相应的产品。工厂和产品的职责区分明确。
  • 客户端无需知道所创建具体产品的类名,只需知道参数即可。
    也可以引入配置文件,在不修改客户端代码的情况下更换和添加新的具体产品类。

缺点

  • 简单工厂模式的工厂类单一,负责所有产品的创建,职责过重,一旦异常,整个系统将受影响。且工厂类代码会非常臃肿,违背高聚合原则。
  • 使用简单工厂模式会增加系统中类的个数(引入新的工厂类),增加系统的复杂度和理解难度
  • 系统扩展困难,一旦增加新产品不得不修改工厂逻辑,在产品类型较多时,可能造成逻辑过于复杂
  • 简单工厂模式使用了 static 工厂方法,造成工厂角色无法形成基于继承的等级结构。
使用场景

对于产品种类相对较少的情况,考虑使用简单工厂模式。使用简单工厂模式的客户端只需要传入工厂类的参数,不需要关心如何创建对象的逻辑,可以很方便地创建所需产品。

工厂方法

工厂方法模式设计方案:将对象的实例化功能抽象成抽象方法,在不同的口味点餐子类中具体实现。

工厂方法模式:定义了一个创建对象的抽象方法,由子类决定要实例化的类。

核心:定义一个抽象方法,让各个工厂子类自己实现。工厂方法模式将对象的实例化推迟到子类

当需要生成的产品不多且不会增加,一个具体工厂类就可以完成任务时,可删除抽象工厂类。这时工厂方法模式将退化到简单工厂模式

模式结构
  • 抽象工厂(Abstract Factory):提供了创建产品的接口,调用者通过它访问具体工厂的工厂方法 newProduct()
    来创建产品。
  • 具体工厂(ConcreteFactory):主要是实现抽象工厂中的抽象方法,完成具体产品的创建。
  • 抽象产品(Product):定义了产品的规范,描述了产品的主要特性和功能。
  • 具体产品(ConcreteProduct):实现了抽象产品角色所定义的接口,由具体工厂来创建,它同具体工厂之间一一对应。
类图

设计模式总结_第17张图片

代码实现

使用工厂方法模式生产USB接口:手机usb接口;电脑USB接口
分别对应打印机,u盘

设计模式总结_第18张图片

public class ComputerUSB  extends  OrderUsb{

    public ComputerUSB(String orderType) {
        super(orderType);
    }

    @Override
    public USBInterface getUSB() {
        System.out.println("电脑USB接口");
        return  UsbFactory.create(orderType);

    }
}

//这里的ComputerUSB PhoneUSB都是具体的工厂
public class FactoryMethodTest {

    public static void main(String[] args) {

        ComputerUSB orderUsb = new ComputerUSB("Printers");
        USBInterface usb = orderUsb.getUSB();
        usb.start();
        usb.stop();
        PhoneUSB orderUsb1 = new PhoneUSB("Flash");
        USBInterface usb1 = orderUsb1.getUSB();
        usb1.start();
        usb1.stop();
    }
}

 public class Flash implements USBInterface {

    @Override
    public void start() {

        System.out.println("U盘开始传输数据");
    }

    @Override
    public void stop() {
        System.out.println("U盘被拔出");

    }

}

public abstract  class OrderUsb {

    String orderType;

    public OrderUsb(String orderType) {

        this.orderType = orderType;

    }

    public abstract  USBInterface  getUSB();


}

public class PhoneUSB extends OrderUsb {

    public PhoneUSB(String orderType) {
        super(orderType);
    }

    @Override
    public USBInterface getUSB() {
        System.out.println("手机USB接口");
        return UsbFactory.create(orderType);

    }
}
public class Printers implements USBInterface {

    @Override
    public void start() {
        System.out.println("打印机开始工作");

    }

    @Override
    public void stop() {
        System.out.println("打印机停止工作");

    }
}

public class UsbFactory {

    public static  USBInterface create(String type) {

       return switch (type) {
            case "Flash" -> new Flash();
            case "Printers" -> new Printers();
            default -> new Flash();
        };
    }
}

interface USBInterface {

    void start();

    void stop();
}

抽象工厂模式

抽象工厂模式:定义了一个interface用于创建相关或有依赖关系的对象簇,而无需指明具体的类

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

概括:提供一个创建产品族的接口,其每个子类可以生产一系列相关的产品。

抽象工厂模式是工厂方法模式的升级版本,工厂方法模式只生产一个等级的产品,而抽象工厂模式可生产多个等级的产品。

模式结构
  • 抽象工厂(Abstract Factory):提供了创建产品的接口,它包含多个创建产品的方法 newProduct(),可以创建多个不同等级的产品。
  • 具体工厂(Concrete Factory):主要是实现抽象工厂中的多个抽象方法,完成具体产品的创建。
  • 抽象产品(Product):定义了产品的规范,描述了产品的主要特性和功能,抽象工厂模式有多个抽象产品。
  • 具体产品(ConcreteProduct):实现了抽象产品角色所定义的接口,由具体工厂来创建,它同具体工厂之间是多对一的关系。
类图

设计模式总结_第19张图片

模式的应用场景
  • 当需要创建的对象是一系列相互关联或相互依赖的产品族时,如电器工厂中的电视机、洗衣机、空调等。
  • 系统中有多个产品族,但每次只使用其中的某一族产品。如有人只喜欢穿某一个品牌的衣服和鞋。
  • 系统中提供了产品的类库,且所有产品的接口相同,客户端不依赖产品实例的创建细节和内部结构。
与工厂模式的区别

区别在于产品,如果产品单一,最合适用工厂模式,但是如果有多个业务品种、业务分类时,通过抽象工厂模式产生需要的对象是一种非常好的解决方式。再通俗深化理解下:工厂模式针对的是一个产品等级结构 ,抽象工厂模式针对的是面向多个产品等级结构的。

设计模式总结_第20张图片

与建造者模式的区别

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

代码实现

使用抽象工厂模式改写USB接口生产,加入生产地伦敦和纽约

设计模式总结_第21张图片

public class AbstractFactoryTest {
    //1.产品接口--2工厂接口(抽象工厂)--3具体工厂--4产品
    public static void main(String[] args) {
        CreateFactory londonUSBFactory = new LondonUSBFactory();


        ComputerUSB orderUsb = new ComputerUSB(londonUSBFactory);
        orderUsb.getUSB("Flash");



        CreateFactory newYorkUSBFactory = new NewYorkUSBFactory();

        PhoneUSB phoneUSB = new PhoneUSB(newYorkUSBFactory);
        phoneUSB.getUSB("Printers");

    }
}
public class ComputerUSB  extends OrderUsb {

    public ComputerUSB(CreateFactory factory) {
        super(factory);
    }

    @Override
    public void getUSB(String type) {

        USBInterface usbInterface = factory.USBFactory(type);
        System.out.println("的电脑USB接口");
        usbInterface.start();
        usbInterface.stop();
    }
}
public interface CreateFactory {

    USBInterface  USBFactory(String type);

}

 public class Flash implements USBInterface {

    @Override
    public void start() {

        System.out.println("U盘开始传输数据");
    }

    @Override
    public void stop() {
        System.out.println("U盘被拔出");

    }

}

public class LondonUSBFactory implements CreateFactory {

    @Override
    public USBInterface USBFactory(String type) { //伦敦工厂生产对应的USB
        System.out.println("伦敦USB工厂生产:");
        return UsbFactory.create(type);
    }
}

public class NewYorkUSBFactory implements  CreateFactory{
    @Override
    public USBInterface USBFactory(String type) {
        System.out.println("纽约USB工厂生产:");
        return UsbFactory.create(type);
    }
}
public abstract  class OrderUsb {

    CreateFactory factory;

    public OrderUsb(CreateFactory factory) {

        this.factory = factory;
    }

    public abstract  void  getUSB(String type);


}

public class PhoneUSB extends OrderUsb {

    public PhoneUSB(CreateFactory factory) {
        super(factory);
    }

    @Override
    public void getUSB(String type) {

        USBInterface usbInterface = factory.USBFactory(type);
        System.out.println("的手机USB接口");
        usbInterface.start();
        usbInterface.stop();
    }
}
public class Printers implements USBInterface {

    @Override
    public void start() {
        System.out.println("打印机开始工作");

    }

    @Override
    public void stop() {
        System.out.println("打印机停止工作");

    }
}
public class UsbFactory {

    public static  USBInterface create(String type) {

       return switch (type) {
            case "Flash" -> new Flash();
            case "Printers" -> new Printers();
            default -> new Flash();
        };
    }
}

interface USBInterface  {

    void start();

    void stop();
}

结构型模式

适配器模式

适配器模式

桥接模式

桥接模式(Bridge模式)是指:将实现与抽象放在两个不同的类层次中,使两个次可层以改变。

基于类的最小设计原则,通过使用封装、聚合及继承等行为让不同的类承担不同的职责。它的主要特点是把抽象(Abstraction)与行为实现分离开来,从而可以保持各部分的独立性以及应对他们的功能扩展

概括:将抽象与实现分离,使它们可以独立变化。它是用组合关系代替继承关系来实现,从而降低了抽象和实现这两个可变维度的耦合度

核心
抽象层与实现层分离,使两个次可层以独立改变,通过聚合进行桥接,关键是区分抽象层和实现层;桥接模式替代多层继承方案,可以减少子类的个数

模式结构
  1. 抽象化(Abstraction)角色:定义抽象类,并包含一个对实现化对象的引用。

  2. 扩展抽象化(RefinedAbstraction)角色:是抽象化角色的子类,实现父类中的业务方法,并通过组合关系调用实现化角色中的业务方法。

  3. 实现化(Implementor)角色:定义实现化角色的接口,供扩展抽象化角色调用。

  4. 具体实现化(Concrete Implementor)角色:给出实现化角色接口的具体实现。

类图

设计模式总结_第22张图片
设计模式总结_第23张图片

优缺点

优点

  • 抽象与实现分离,扩展能力强, 替代多层继承方案,可以减少子类的个数,降低系统的管理和维护成本
  • 符合开闭原则
  • 符合合成复用原则
  • 其实现细节对客户透明
  • 对于系统的高层部分,只需要知道抽象部分和实现部分的接口就可以了,其它的部分由具体业务来完成

缺点

  • 由于聚合关系建立在抽象层,要求开发者针对抽象化进行设计与编程,能正确地识别出系统中两个独立变化的维度,这增加了系统的理解与设计难度。
应用场景

对于那些不希望使用继承或因为多层次继承导致系统类的个数急剧增加的系统,桥接模式尤为适用. 如:

  • JDBC驱动程序
  • -银行转账系统
    转账分类: 网上转账,柜台转账,AMT转账
    转账用户类型:普通用户,银卡用户,金卡用户…
  • -消息管理
    消息类型:即时消息,延时消息
    消息分类:手机短信,邮件消息,QQ消息…
JDBC 中的桥接模式

Jdbc 的 Driver接口,如果从桥接模式来看,Driver就是一个接口,下面可以有
MySQL的Driver,Oracle的Driver,这些就可以当做实现接口类
设计模式总结_第24张图片
设计模式总结_第25张图片

示例代码

使用桥接模式模拟消息管理

设计模式总结_第26张图片

public class BridgingPattern {

    public static void main(String[] args) {



         QQMessage qqMessage = new  QQMessage(new DelayedMessage());

         qqMessage.sendMessage();
         qqMessage.getMessage();



         WeiXinMessage weiXinMessage = new WeiXinMessage( new timelyMessage());
         weiXinMessage.sendMessage();
         weiXinMessage.getMessage();

    }

}

public class DelayedMessage implements  Message{
    @Override
    public void sendMessage() {
        System.out.println("发送延时消息");
    }

    @Override
    public void getMessage() {
        System.out.println("接收延时消息");
    }
}

public interface Message {

    void  sendMessage();
    void  getMessage();
}

public  abstract  class messageTypeAbstract {

    protected Message message;

    public messageTypeAbstract(Message message) {
        this.message = message;
    }

    abstract void  sendMessage();
    abstract void  getMessage();
}

public class QQMessage extends messageTypeAbstract{

    public QQMessage(Message message) {
        super(message);
    }

    @Override
    void sendMessage() {
        System.out.println("QQ:");
        message.sendMessage();
    }

    @Override
    void getMessage() {
        System.out.println("QQ:");
       message.getMessage();
    }
}
public class timelyMessage implements  Message{


    @Override
    public void sendMessage() {
        System.out.println("发送及时消息");
    }

    @Override
    public void getMessage() {
        System.out.println("接收及时消息");
    }
}
public class WeiXinMessage extends messageTypeAbstract{

    public WeiXinMessage(Message message) {
        super(message);
    }

    @Override
    void sendMessage() {
        System.out.println("微信:");
         message.sendMessage();
    }

    @Override
    void getMessage() {
        System.out.println("微信:");
        message.getMessage();
    }
}

装饰者模式

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

动态的将新功能附加到对象和符和ocp原则

概括:动态的给对象增加一些职责,即增加其额外的功能。
核心:动态的将新功能附加到对象上;主体:被装饰者,包装:装饰者

模式结构
  • 抽象构件(Component)角色:定义一个抽象接口以规范准备接收附加责任的对象。
  • 具体构件(ConcreteComponent)角色:实现抽象构件,通过装饰角色为其添加一些职责。
  • 抽象装饰(Decorator)角色:继承抽象构件,并包含具体构件的实例,可以通过其子类扩展具体构件的功能。
  • 具体装饰(ConcreteDecorator)角色:实现抽象装饰的相关方法,并给具体构件对象添加附加的责任
类图

设计模式总结_第27张图片
设计模式总结_第28张图片

IO结构中的装饰者模式

IO 中的FilterInputStream就是一个装饰者
设计模式总结_第29张图片

优缺点

优点

  • 装饰器是继承的有力补充,比继承灵活,在不改变原有对象的情况下,动态的给一个对象扩展功能,即插即用
  • 通过使用不用装饰类及这些装饰类的排列组合,可以实现不同效果
  • 装饰器模式完全遵守开闭原则
  • 无需创建新子类即可扩展对象的行为
  • 单一职责原则。 可以将实现了许多不同行为的一个大类拆分为多个较小的类

缺点

  • 装饰器模式会增加许多子类,过度使用会增加程序得复杂性。
  • 实现行为不受装饰的顺序影响,装饰比较困难
应用场景
  • 如果你希望在无需修改代码的情况下即可使用对象, 且希望在运行时为对象新增额外的行为, 可以使用装饰模式。
  • 当需要通过对现有的一组基本功能进行排列组合而产生非常多的功能时,采用继承关系很难实现,而采用装饰器模式却很好实现。
  • 当对象的功能要求可以动态地添加,也可以再动态地撤销时
示例代码

使用装饰者模式购买不同种类的咖啡
设计模式总结_第30张图片

public class DecoratorPattern {

    public static void main(String[] args) {

          CoffeeAbstract espressoCoffee = new EspressoCoffee(5.0f,"意大利咖啡");

          espressoCoffee = new Milk(espressoCoffee,1.0f,"牛奶");

          System.out.println(espressoCoffee.getTotalPrice());
          System.out.println(espressoCoffee.getDes());

          espressoCoffee  = new Soy(espressoCoffee,0.5f,"豆浆");
         System.out.println(espressoCoffee.getTotalPrice());
         String des = espressoCoffee.getTotalDes();
         System.out.println(des);
    }
}

public abstract class Coffee extends CoffeeAbstract{


    public float getTotalPrice(){

        return  super.getPrice();
    }
}
public abstract class CoffeeAbstract {

    String des;

    float price;

    public String getDes() {
        return des;
    }

    public void setDes(String des) {
        this.des = des;
    }

    public float getPrice() {
        return price;
    }

    public void setPrice(float price) {
        this.price = price;
    }

    abstract  float getTotalPrice();
    abstract  String getTotalDes();
}
public class EspressoCoffee  extends  CoffeeAbstract{

    public  EspressoCoffee(float price,String description){
         super();
         setPrice(price);
         setDes(description);
    }

    @Override
    float getTotalPrice() {
        return super.price;
    }

    @Override
    String getTotalDes() {
        return getDes();
    }
}
public class IngredientDecorator  extends CoffeeAbstract{

    private final CoffeeAbstract coffee;

    public IngredientDecorator(CoffeeAbstract coffee){
        this.coffee = coffee;
    }

    @Override
    float getTotalPrice() {
        return  price + coffee.getTotalPrice();
    }

    @Override
    public String getTotalDes() {
        return coffee.getTotalDes() + coffee.getPrice() +
                des +
                getPrice();
    }


}

public class LongBlackCoffee  extends  Coffee{
    float price;

    public  LongBlackCoffee(float price){
        super();
        this.price = price;
    }

    @Override
    String getTotalDes() {
        return getDes();
    }
}

public class Milk  extends IngredientDecorator{

    public Milk(CoffeeAbstract coffee,float price,String description) {
        super(coffee);
        setPrice(price);
        setDes(description);
    }

}

public class Soy  extends  IngredientDecorator{

    public Soy(CoffeeAbstract coffee,float price,String description) {
        super(coffee);
        setPrice(price);
        setDes(description);
    }

}

组合模式

组合模式是一种结构型设计模式,又叫部分整体模式。 可以使用它将对象组合成树状结构,用来表示部分以及整体层次, 并且能像使用独立对象一样使用它们。

组合模式使得用户对单个对象和组合对象的访问具有一致性,即 :组合能让客户以一致的方式处理个别对象以及组合对象

概括:将对象组合成树状层次结构,使用户对单个对象和组合对象具有一致的访问性

模式结构
  • 抽象构件(Component)角色:它的主要作用是为树叶构件和树枝构件声明公共接口,并实现它们的默认行为。在透明式的组合模式中抽象构件还声明访问和管理子类的接口;在安全式的组合模式中不声明访问和管理子类的接口,管理工作由树枝构件完成。(总的抽象类或接口,定义一些通用的方法,比如新增、删除)
  • 树叶构件(Leaf)角色:是组合中的叶节点对象,它没有子节点,用于继承或实现抽象构件。
  • 树枝构件(Composite)角色 / 中间构件:是组合中的分支节点对象,它有子节点,用于继承和实现抽象构件。它的主要作用是存储和管理子部件,通常包含 Add()、Remove()、GetChild() 等方法
类图

设计模式总结_第31张图片
设计模式总结_第32张图片

优缺点

优点

  • 组合模式使得客户端代码可以一致地处理单个对象和组合对象,无须关心自己处理的是单个对象,还是组合对象,这简化了客户端代码;
  • 更容易在组合体内加入新的对象,客户端不会因为加入了新的对象而更改源代码,满足“开闭原则”;

缺点

  • 设计较复杂,客户端需要花更多时间理清类之间的层次关系;
  • 不容易限制容器中的构件;
  • 不容易用继承的方法来增加构件的新功能;
  • 对于功能差异较大的类, 提供公共接口或许会有困难。 在特定情况下, 需要过度一般化组件接口, 使其变得令人难以理解。
应用场景
  • 当我们的要处理的对象可以生成一颗树形结构,而我们要对树上的节点和叶子进行操作时,它能够提供一致的方式,而不用考虑它是节点还是叶子
  • 需要遍历组织机构,或者处理的对象具有树形结构时
  • 如果希望客户端代码以相同方式处理简单和复杂元素, 可以使用该模式。
  • 如果节点和叶子有很多差异性的话 ,比如很多方法和属性都不一样,不适合使用组合模式
示例代码

使用组合模式处理学校 学院 系别 的关系

设计模式总结_第33张图片

public class College implements OrganizationComponent {

    List<OrganizationComponent> organizationComponents = new ArrayList<>();//组合

    String name;
    String des;

    public  College(String name, String des){
        this.name = name;
        this.des = des;
    }
    @Override
    public void add(OrganizationComponent component) {
        organizationComponents.add(component);
    }


    @Override
    public void remove(OrganizationComponent component) {
        organizationComponents.remove(component);
    }

     public void print(){
         System.out.println("--------------" +name + "--------------");

         for (OrganizationComponent organizationComponent : organizationComponents) {
             organizationComponent.print();
         }
    }
}

public class CombinationPattern {
    public static void main(String[] args) {

        OrganizationComponent university = new University("北京大学", "这是北京大学");

        College college = new College("计算机学院", "顶级院系");
        College college1 = new College("信息工程学院", "信息工程学院");

        college.add(new Department("计算机科学与技术", "计算机科学与技术"));
        college.add(new Department("网络工程", "网络工程"));
        college.add(new Department("软件工程", "软件工程"));
        college1.add(new Department("通讯工程", "通讯工程"));
        college1.add(new Department("信息工程", "信息工程"));

        university.add(college);
        university.add(college1);

        university.print();
    }
}
public class Department implements  OrganizationComponent{


    String name;
    String des;

    public Department(String name, String des){
        this.name = name;
        this.des = des;
    }

    public  void  print(){
        System.out.println(name);
    }
}
public  interface OrganizationComponent {

    default void  add(OrganizationComponent component){

    }
    default void remove(OrganizationComponent component){

    }

    void  print();
}
public class University implements OrganizationComponent {

    List<OrganizationComponent> organizationComponents = new ArrayList<>();

    String name;
    String des;

    public University(String name, String des){
        this.name = name;
        this.des = des;
    }
    @Override
    public void add(OrganizationComponent component) {
        organizationComponents.add(component);
    }


    @Override
    public void remove(OrganizationComponent component) {
        organizationComponents.remove(component);
    }

    public void print(){
        System.out.println("--------------" +name + "--------------");

        for (OrganizationComponent organizationComponent : organizationComponents) {
            organizationComponent.print();
        }
    }
}

外观模式

外观模式是一种结构型设计模式, 能为程序库、 框架或其他复杂类提供一个简单的接口。

我们都在有意无意的大量使用外观模式。只要是高层模块需要调度多个子系统(2个以上的类对象),我们都会自觉地创建一个新的类封装这些子系统,提供精简的接口,让高层模块可以更加容易地间接调用这些子系统的功能。尤其是现阶段各种第三方SDK、开源类库,很大概率都会使用外观模式。

  1. 外观模式(Facade),也叫“过程模式:外观模式为子系统中的一组接口提供
    一个一致的界面,此模式定义了一个高层接口,这个接口使得这一子系统更加容易使用
  2. 外观模式通过定义一个一致的接口,用以屏蔽内部子系统的细节,使得调用端只需跟这个接口发生调用,而无需关心这个子系统的内部细节
  3. 外观模式可以理解为转换一群接口,客户只要调用一个接口,而不用调用多个接口才能达到目的。
    比如:在pc上安装软件的时候经常有一键安装选项(省去选择安装目录、安装的组件等等),还有就是手机的重启功能(把关机和启动合为一个操作)。
  4. 外观模式就是解决多个复杂接口带来的使用困难,起到简化用户操作的作用

概括:为多个复杂的子系统提供一个一致的接口,使这些子系统更加容易被访问
核心:使用高层次的抽象类或者接口来组合使用复杂的子系统,是使用者调用子系统简介化,无需关心子系统的内部细节。

模式结构
  • 外观(Facade)模式包含以下主要角色。
  • 外观(Facade)角色:为多个子系统对外提供一个共同的接口。
  • 子系统(Sub System)角色:实现系统的部分功能,客户可以通过外观角色访问它。
    客户(Client)角色:通过一个外观角色访问各个子系统的功能。
类图

设计模式总结_第34张图片

设计模式总结_第35张图片

优缺点

优点

  • 降低了子系统与客户端之间的耦合度,使得子系统的变化不会影响调用它的客户类。让自 己的代码独立于复杂子系统
  • 对客户屏蔽了子系统组件,减少了客户处理的对象数目,并使得子系统使用起来更加容易维护和扩展。
  • 降低了大型软件系统中的编译依赖性,简化了系统在不同平台之间的移植过程,因为编译一个子系统不会影响其他的子系统,也不会影响外观对象。

缺点

  • 不能很好地限制客户使用子系统类,很容易带来未知风险。
  • 增加新的子系统可能需要修改外观类或客户端的源代码,违背了“开闭原则”
MyBatis中的外观模式

MyBatis 中的Configuration 去创建MetaObject 对象使用到外观模式
设计模式总结_第36张图片
设计模式总结_第37张图片

应用场景
  • 当一个指向复杂子系统的直接接口, 且该接口的功能有限, 则可以使用外观模式。
  • 如果需要将子系统组织为多层结构(当系统需要进行分层设计时), 可以使用外观模式。
示例代码

使用外观模式模拟用户去医院看病过程
设计模式总结_第38张图片

public class Bargain {
    public  void  bargain(){

        System.out.println("病人开始划价");
    }
}
public class Client {
    public static void main(String[] args) {

        ReceptionistFacade receptionistFacade = new ReceptionistFacade();

        receptionistFacade.receptionist(new User("Tom"));

    }
}
public class OutpatientDepartment {

    public  void  outpatientDepartment(){

        System.out.println("病人开始门诊");
    }
}
public class ReceptionistFacade {

     private final Bargain bargain = new Bargain();
     private final OutpatientDepartment outpatientDepartment = new OutpatientDepartment();
     private final Register register = new Register();
     private final TakeMedicine takeMedicine = new TakeMedicine();

     public void receptionist(User user){

         System.out.println("接待员开始接待"+user.name);
         register.register();
         outpatientDepartment.outpatientDepartment();
         bargain.bargain();
         takeMedicine.takeMedicine();
     }

}
public class Register {
    public  void  register(){

        System.out.println("病人开始挂号");
    }
}
public class TakeMedicine {
    public  void  takeMedicine(){

        System.out.println("病人开始取药");
    }
}
public class User {
    public  String name;

    public User(String name){
        this.name = name;
    }
}

享元模式

用共享技术来有效地支持大量细粒度对象的复用。它通过共享已经存在的对象来大幅度减少需要创建的对象数量、避免大量相似类的开销,从而提高系统资源的利用率。

常用于系统底层开发,解决系统的性能问题。像数据库连接池,里面都是创建好的连接对象,这些连接对象中有我们需要的则直接拿来用,避免重新创建,如果没有我们需要的,则创建一个

享元模式能够解决 重复对象的内存浪费的问题,当系统中有大量相似对象,需要缓冲池时。不需总是创建新对象,可以从缓冲池里拿。这样可以降低系统内存,同时提高效率

概括:运用共享技术来有效地支持大量细粒度对象的复用,有就共享,没有就创建

享元模式提出了两个要求:细粒度和共享对象。这里就涉及到内部状态和外部状态了,即将对象的信息分为两个部分:内部状态和外部状态
内部状态(共享状态)指对象共享出来的信息,存储在享元对象内部且不会随环境的改变而改变
外部状态(非共享状态)指对象得以依赖的一个标记,是随环境改变而改变的、不可共享的状态。

模式结构
  • 抽象享元角色(Flyweight):是所有的具体享元类的基类,为具体享元规范需要实现的公共接口,非享元的外部状态以参数的形式通过方法传入。
  • 具体享元(Concrete Flyweight)角色:实现抽象享元角色中所规定的接口。
  • 非享元(Unsharable Flyweight)角色:是不可以共享的外部状态,它以参数的形式注入具体享元的相关方法中。
  • 享元工厂(Flyweight Factory)角色:负责创建和管理享元角色。当客户对象请求一个享元对象时,享元工厂检査系统中是否存在符合要求的享元对象,如果存在则提供给客户;如果不存在的话,则创建一个新的享元对象。
类图

设计模式总结_第39张图片
设计模式总结_第40张图片

优缺点

优点

  • 如果程序中有很多相似对象, 使用享元模式可以节省大量内存。降低了系统中细粒度对象给内存带来的压力。

缺点

  • 为了使对象可以共享,需要将一些不能共享的状态外部化,这将增加程序的复杂性

  • 共享状态和非共享状态需要正确区分出来

  • 读取享元模式的外部状态会使得运行时间稍微变长

应用场景
  • 需要缓冲池的场景,比如 String常量池、数据库连接池
  • 程序需要生成数量巨大的相似对象
  • 对象中包含可抽取且能在多个对象间共享的重复状态
示例代码

使用享元模式模拟图书馆借书流程
设计模式总结_第41张图片

public  class LibraryFlyweightFactory {


    HashMap<String,BookFlyweight> hashMap = new HashMap<>();


    public  BookFlyweight getBook(String bookName){

        if (!hashMap.containsKey(bookName)){

//            hashMap.put(bookName, new BookFlyweight(bookName) {
//                @Override
//                void borrow(User user) {
//                    System.out.println(user.name  + "借书"+bookName);
//                }
//            });
            hashMap.put(bookName, new ConcreteBookA(bookName));
        }

        return hashMap.get(bookName);
    }

    public int size() {
        return hashMap.size();
    }

}

public class FlyweightPattern {
    public static void main(String[] args) {
        LibraryFlyweightFactory factory = new LibraryFlyweightFactory();

        BookFlyweight bookA  = factory.getBook("设计模式");
        BookFlyweight bookB  = factory.getBook("数据结构");
        BookFlyweight bookC  = factory.getBook("JVM");

        bookA.borrow(new User("tom"));
        bookA.borrow(new User("jack"));
        bookB.borrow(new User("leo"));
        bookC.borrow(new User("ben"));
        System.out.println(factory.size());

    }
}

public class ConCreteBookB  extends  BookFlyweight{


    public  ConCreteBookB(String bookName) {
        super(bookName);
    }

    @Override
    public void borrow(User user) {
        System.out.println(user.name  + "借书B");
    }
}

public class ConcreteBookA  extends BookFlyweight{



    public   ConcreteBookA(String bookName) {
        super(bookName);


    }
    @Override
    public  void borrow(User user) {

        System.out.println(user.name  + "借书 "+bookName);
    }
}

public abstract class BookFlyweight {

     String bookName;

     public BookFlyweight(String bookName) {
          this.bookName = bookName;
     }

     abstract void borrow(User user);

}

代理模式

代理模式

行为型模式

模版模式

模板方法模式是一种行为设计模式, 它在超类中定义了一个算法的框架, 允许子类在不修改结构的情况下重写算法的特定步骤。

模板方法模式在一个抽象类公开定义了执行它的方法的模板。它的子类可以按需要重写方法实现,但调用将以抽象类中定义的方式进行

概括:定义一个操作中的算法骨架,而将算法的一些步骤延迟到子类中,使得子类可以不改变该算法结构的情况下重定义该算法的某些特定步骤。

一般模板方法都加上final关键字, 防止子类重写模板方法

模式结构
  • 模板方法:定义了算法的骨架,按某种顺序调用其包含的基本方法。

  • 基本方法:是整个算法中的一个步骤,包含以下几种类型。
    抽象方法:在抽象类中声明,由具体子类实现。
    具体方法:在抽象类中已经实现,在具体子类中可以继承或重写它。
    钩子方法:在抽象类中已经实现,包括用于判断的逻辑方法和需要子类重写的空方法两种

类图

设计模式总结_第42张图片
设计模式总结_第43张图片

Spring框架中的模板模式

Spring IOC容器初始化时运用到的模板方法模式

设计模式总结_第44张图片

优缺点

优点

  • 可将重复代码提取到一个超类中
  • 部分方法是由子类实现的,因此子类可以通过扩展方式增加相应的功能,符合开闭原则。
    -允许客户端重写一个大型算法中的特定部分, 使得算法其他部分修改对其所造成的影响减小

缺点

  • 部分客户端可能会受到算法框架的限制
  • 通过子类抑制默认步骤实现可能会导致违反里氏替换原则
  • 由于继承关系自身的缺点,如果父类添加新的抽象方法,则所有子类都要改一遍
  • 模板方法中的步骤越多, 其维护工作就可能会越困难
  • 父类中的抽象方法由子类实现,子类执行的结果会影响父类的结果,这导致一种反向的控制结构,它提高了代码阅读的难度。
应用场景
  • 只希望客户端扩展某个特定算法步骤, 而不是整个算法或其结构时, 可使用模板方法模式。

  • 当需要控制子类的扩展时,模板方法只在特定点调用钩子操作,这样就只允许在这些点进行扩展。

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

示例代码

使用模板模式制作豆浆过程
设计模式总结_第45张图片

public class TemplatePattern {

    public static void main(String[] args) {

        AbstractTemplate milk = new MungBeanMilk();
        milk.make();
        System.out.println("*******************");

        AbstractTemplate redSoybeanMilk = new RedSoybeanMilk();
        redSoybeanMilk.make();
        System.out.println("*******************");

        AbstractTemplate sugarFreeBeanMilk = new SugarFreeBeanMilk();
        sugarFreeBeanMilk.make();
    }
}
public abstract class AbstractTemplate {

    public final   void  make(){
        selected();
        if (isAdd())
        add();
        soak();
        stir();
    }

    protected  void  selected(){

        System.out.println("选材料");
    }

    abstract   void  add();

    protected  void  soak(){
        System.out.println("浸泡");
    }
    protected  void  stir(){
        System.out.println("搅拌");
    }

    protected  boolean isAdd(){

        return true;
    }
}

public class MungBeanMilk  extends  AbstractTemplate  {
    @Override
    void add() {
        System.out.println("加入绿豆");
    }
}
public class RedSoybeanMilk extends  AbstractTemplate{
    @Override
    void add() {

        System.out.println("加入红豆");
    }
}
public class SugarFreeBeanMilk extends  AbstractTemplate{
    @Override
    void add() {
        System.out.println("加糖");
    }
    public boolean isAdd(){
        return false;
    }
}

解释器模式

是指给定一个语言(表达式),定义它的文法的一种表示,并定义一个解释器,使用该解释器来解释语言中的句子(表达式)

概括:提供如何定义语言的文法,以及对语言句子的解释方法,即解释器。

模式结构
  • 抽象表达式(Abstract Expression)角色:定义解释器的接口,约定解释器的解释操作,主要包含解释方法 interpret()。
  • 终结符表达式(Terminal Expression)角色:是抽象表达式的子类,用来实现文法中与终结符相关的操作,文法中的每一个终结符都有一个具体终结表达式与之相对应。
  • 非终结符表达式(Nonterminal Expression)角色:也是抽象表达式的子类,用来实现文法中与非终结符相关的操作,文法中的每条规则都对应于一个非终结符表达式。
  • 环境(Context)角色:通常包含各个解释器需要的数据或是公共的功能,一般用来传递被所有解释器共享的数据,后面的解释器可以从这里获取这些值。
  • 客户端(Client):主要任务是将需要分析的句子或表达式转换成使用解释器对象描述的抽象语法树,然后调用解释器的解释方法,当然也可以通过环境角色间接访问解释器的解释方法。
类图

设计模式总结_第46张图片

优缺点

优点

扩展性好。由于在解释器模式中使用类来表示语言的文法规则,因此可以通过继承等机制来改变或扩展文法。

缺点

  • 执行效率较低。解释器模式中通常使用大量的循环和递归调用,当要解释的句子较复杂时,其运行速度很慢,且代码的调试过程也比较麻烦。
  • 可应用的场景比较少。在软件开发中,需要定义语言文法的应用实例非常少,所以这种模式很少被使用到。
应用场景
  • 应用可以将一个需要解释执行的语言中的句子表示为一个抽象语法树。
  • 一个简单语法需要解释的场景。
  • 一些重复出现的问题可以用一种简单的语言来表达
Spring框架中的解释器模式

Spring框架中 SpelExpressionParser就使用到解释器模式
设计模式总结_第47张图片

  • Expression 接口表达式接口
  • 下面有不同的实现类,比如SpelExpression, 或者CompositeStringExpression。
  • 使用时候,根据你创建的不同的Parser 对象,返回不同的 Expression 对象。
示例代码

设计模式总结_第48张图片

public abstract class AbstractExpression {

    abstract boolean  interpret(String string);
}
public class Context {

    private final AbstractExpression cityPerson;
    public Context(String[] citys,String[] persons) {
        AbstractExpression city = new TerminalExpression(citys);
        AbstractExpression person = new TerminalExpression(persons);
        cityPerson = new NoTerminalExpression(city, person);
    }

    public void freeRide(String info) {
        boolean ok = cityPerson.interpret(info);
        if (ok) System.out.println("您是" + info + ",您本次乘车免费!");
        else System.out.println(info + ",您不是免费人员,本次乘车扣费2元!");
    }
}
public class InterpreterPattern {
    public static void main(String[] args) {
        Context bus = new Context(new String[]{"韶关", "广州"},new String[] {"老人", "妇女", "儿童"});
        bus.freeRide("韶关的老人");
        bus.freeRide("韶关的年轻人");
        bus.freeRide("广州的妇女");
        bus.freeRide("广州的儿童");
        bus.freeRide("山东的儿童");
    }
}
public class NoTerminalExpression extends  AbstractExpression{

    private final AbstractExpression city;
    private final AbstractExpression person;
    public NoTerminalExpression(AbstractExpression city, AbstractExpression person) {
        this.city = city;
        this.person = person;
    }
    @Override
    public boolean interpret(String info) {
        String[] s = info.split("的");
        return city.interpret(s[0]) && person.interpret(s[1]);
    }
}
public class TerminalExpression extends  AbstractExpression{
    private final Set<String> set = new HashSet<>();
    public TerminalExpression(String[] data) {
        Collections.addAll(set, data);
    }
    public boolean interpret(String info) {
        return set.contains(info);
    }
}

命令模式

命令模式

访问者模式

访问者模式

迭代器模式

迭代器模式

观察者模式

观察者模式

中介者模式

中介者模式

备忘录模式

备忘录模式

状态模式

状态模式

策略模式

策略模式

职责链模式

职责链模式

你可能感兴趣的:(设计模式,java,设计模式,设计模式汇总)