# 设计模式学习
**Repository Path**: fpcloud/design-pattern-learning
## Basic Information
- **Project Name**: 设计模式学习
- **Description**: 设计模式学习
- **Primary Language**: Java
- **License**: Not specified
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 0
- **Created**: 2021-07-09
- **Last Updated**: 2021-09-14
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# 设计模式学习
源码编号解析:design-item-demoId-typeId
+ **demoId:** 设计模式编号:工厂方法模式(01):design-itme-01-...
+ **typeId:**
1. **00:** 代表模拟的场景(scene.md)
2. **01:** if-else实现方式
3. **02:** 实际模式实现方式
****
## 创建者模式(5种)
**定义:** 创建型模式提供了创建对象的机制, 能够提升已有代码的灵活性和可复用性。
工厂方法模式 抽象工厂模式 建造者模式 原型模式 单例模式
****
### 工厂方法模式(01)
**亦称:** 虚拟构造函数、Virtual Constructor、Factory Method
+ **什么是工厂模式?**
>工厂方法模式是一种创建型设计模式, 其在父类中提供一个创建对象的方法, 允许子类决定实例化对象的类型。
>这种设计模式也是 Java 开发中最常⻅的⼀种模式,它的主要意图是定义⼀个创建对象的接⼝,让其⼦
类⾃⼰决定实例化哪⼀个⼯⼚类,⼯⼚模式使其创建过程延迟到⼦类进⾏。
>简单说就是为了提供代码结构的扩展性,屏蔽每⼀个功能类中的具体实现逻辑。让外部可以更加简单的
只是知道调⽤即可,同时,这也是去掉众多 if else 的⽅式。
+ **应用场景:**
> - 当你在编写代码的过程中, 如果无法预知对象确切类别及其依赖关系时, 可使用工厂方法。
> - 如果你希望用户能扩展你软件库或框架的内部组件, 可使用工厂方法。
> - 如果你希望复用现有对象来节省系统资源, 而不是每次都重新创建对象, 可使用工厂方法。
+ **工厂模式优缺点?**
1.优点:
> - 你可以避免创建者和具体产品之间的紧密耦合。
> - 单一职责原则。 你可以将产品创建代码放在程序的单一位置, 从而使得代码更容易维护。
> - 开闭原则。 无需更改现有客户端代码, 你就可以在程序中引入新的产品类型。
2.缺点:
> - 应用工厂方法模式需要引入许多新的子类, 代码可能会因此变得更复杂。 最好的情况是将该模式引入创建者类的现有层次结构中。
+ **与其他设计模式关系**
> - 在许多设计工作的初期都会使用***工厂方法模式***(较为简单, 而且可以更方便地通过子类进行定制),随后演化为使用***抽象工厂模式***、***原型模式***或***建造者模式***(更灵活但更加复杂)
> - ***抽象工厂模式***通常基于一组***工厂方法***,当你也可以使用***原型模式***来生成这些类得方法
> - 你可以同时使用***工厂方法***和***迭代器模式***来让子类集合返回不同类型得迭代器,并使得迭代器与集合相匹配
> - ***原型***并不基于继承,因此没有继承的缺点。另一方面,原型需要对呗复制对象进行复杂的初始化。***工厂方法***基于继承,但是它不需要初始化步骤。
> - ***工厂方法***是模板方法模式的一种特殊形式。同时,工厂方法可以作为一个大型的模板方法中得一个步骤。
****
### 抽象工厂模式(02)
**亦称:** Abstract Factory
+ **什么是抽象工厂模式?**
> 抽象工厂模式是一种创建型设计模式, 它能创建一系列相关的对象, 而无需指定其具体类。
> 抽象⼯⼚模式与⼯⼚⽅法模式虽然主要意图都是为了解决,接⼝选择问题。但在实现上,抽象⼯⼚是⼀
个中⼼⼯⼚,创建其他⼯⼚的模式
+ **应用场景:**
> - 如果代码需要与多个不同系列的相关产品交互,但是由于无法提前获取相关信息,或者出于对未来扩展性的考虑,你不希望代码基于产品的具体类进行构建,在这种情况下,你可以使用抽象工厂。
> - 如果你有一个基于一组***抽象方法***的类,且其主要功能因此变得不明确,那么在这种情况下可以考虑使用抽象工厂模式
+ **抽象工程模式优缺点:**
1.优点:
> - 你可以确保同一工厂生成的产品相互匹配。
> - 你可以避免客户端和具体产品代码的耦合
> - 单一职责原则。你可以将产品生成代码抽取到同一位置,使得代码易于维护。
> - 开闭原则。像应用程序中引入新产品变体时,你无需修改客户端代码。
2.缺点:
> - 由于采用该模式需要向应用中引入众多接口和类,代码可能会比之前更加复杂
+ **与其他设计模式的关系:**
> - 在许多设计工作的初期都会使用***工厂方法模式*** (较为简单, 而且可以更方便地通过子类进行定制), 随后演化为使用***抽象工厂模式***、 ***原型模式***或***建造者模式*** (更灵活但更加复杂)。
> - ***建造者模式***重点关注如何分步生成复杂对象。 ***抽象工厂***专门用于生产一系列相关对象。 抽象工厂会马上返回产品, 建造者模式则允许你在获取产品前执行一些额外构造步骤。
> - ***抽象工厂模式***通常基于一组***工厂方法***, 但你也可以使用***原型模式***来生成这些类的方法。
> - 当只需对客户端代码隐藏子系统创建对象的方式时, 你可以使用***抽象工厂***来代替***外观模式***。
> - 你可以将***抽象工厂***和***桥接模式***搭配使用。 如果由桥接定义的抽象只能与特定实现合作, 这一模式搭配就非常有用。 在这种情况下, 抽象工厂可以对这些关系进行封装, 并且对客户端代码隐藏其复杂性。
> - ***抽象工厂***、 ***建造者模式***和***原型***都可以用单例模式来实现。
****
### 建造者模式(03)
**亦称:** 生成器模式、Builder
+ **什么是建造者模式?**
> 建造者模式是一种创建型设计模式,使你能够分步骤创建复杂对象。 该模式允许你使用相同的创建代码生成不同类型和形式的对象。
> 简单来说根据相同物料不同组装所产生出的具体内容,就是建造者模式的最终意图,也就是:**将一个复杂的构建与其表示相分离,使得同样的构建过程可以创建不同的表示**
+ **应用场景:**
> - 使用建造者模式可避免 “重叠构造函数 (telescopic constructor)” 的出现。
> - 当你希望使用代码创建不同形式的产品 (例如石头或木头房屋) 时, 可使用建造者模式。
> - 使用建造者模式构造组合树或其他复杂对象。
+ **建造者模式优缺点:**
1.优点:
> - 你可以分步创建对象,暂缓创建步骤或递归运行创建步骤。
> - 生成不同形式的产品时,你可以复用相同的制造代码。
> - 单一职责原则。你可以将复杂构造代码从产品的业务逻辑分离出来
2.缺点:
> - 由于该模式需要新增多个类,因此代码整体复杂程度会有所增加
+ **与其他设计模式的关系:**
> - 在许多设计工作初期都会使用***工厂模式***(较为简单,而且可以更方便的通过子类进行定制),随后演化为使用***抽象工厂模式***、***原型模式***、或***建造者模式***(更灵活但更复杂)
> - ***建造者模式***重点关注如何分步生成复杂对象。***抽象工厂***专门用于生产一系列相关对象。抽象工厂会马上返回产品。建造者则允许你在获取产品前只想一些额外构造步骤。
> - 你可以在创建复杂***组合模式***树时使用***建造者模式***,因为这可使其构造步骤以递归的方式运行。
> - 你可以结合使用***建造者模式***和***桥接模式***:主管类负责抽象工作,各种不同的生成器负责实现工做。
> - ***抽象工厂***、***建造者模式***和***原型***都可以用***单例模式***来实现
****
### 原型模式(04)
**亦称:** 克隆、Clone、Prototype
+ **什么是原型模式?**
> 原型模式是一种创建型设计模式, 使你能够复制已有对象, 而又无需使代码依赖它们所属的类。
> 原型模式主要解决的问题就是创建重复对象,⽽这部分对象内容本身⽐较复杂,⽣成过程可能从库或者
RPC接⼝中获取数据的耗时较⻓,因此采⽤克隆的⽅式节省时间
+ **应用场景:**
> - 如果你需要复制一些对象,同时有希望代码独立于这些对象所属的具体类,可以使用原型模式
> - 如果子类的区别仅在其对象的初始化方式,那么你可以使用该模式来减少子类的数量。别人创建这些子类的目的可能是为了创建特定类型的对象。
> - 使用建造者模式构造组合树或其他复杂对象。
+ **原型模式优缺点:**
1.优点:
> - 你可以克隆对象,而无需与他们所属的具体类相耦合。
> - 你可以克隆预生成原型,避免反复运行初始化代码。
> - 你可以更方便的生成复杂对象。
> - 你可以用继承以外的方式来处理复杂对象的不同配置。
2.缺点:
> - 克隆包含循环引用的复杂对象可能会非常麻烦
+ **与其他设计模式的关系:**
> - 在许多设计工作初期都会使用***工厂模式***(较为简单,而且可以更方便的通过子类进行定制),随后演化为使用***抽象工厂模式***、***原型模式***、或***建造者模式***(更灵活但更复杂)。
> - ***抽象工厂模式***通常基于一组***工厂方法***,但你也可以使用***原型模式***来生成这些类的方法。
> - ***原型***可用于保存***命令模式***的历史记录。
> - 大量使用***组合模式***和***装饰模式***的设计通常可从对于***原型***的使用中获益。你可以通过该模式来复制复杂结构,而非从零开始重新构造。
> - ***原型***并不基于继承,因此没有继承的缺点。另一方面。原型需要对被复制的对象进行复杂的初始化。***工厂方法***基于继承,但它不需要初始化步骤。
> - 有时候***原型***可以作为***备忘录模式***的一个简化版本,其条件是你需要在历史记录中存储的对象的状态比较简单,不需要连接其他外部资源,或者链接可以方便地重建。
> - ***抽象工厂***、***建造者模式***和***原型***都可以用***单例模式***来实现
****
### 单例模式(05)
**亦称:** 单件模式、Singleton
+ **什么是单例模式?**
> 单例模式是一种创建型设计模式, 让你能够保证一个类只有一个实例, 并提供一个访问该实例的全局节点。
> 单例模式主要解决的是,⼀个全局使⽤的类频繁的创建和消费,从⽽提升提升整体的代码的性能。
+ **应用场景:**
> - 如果程序中的某个类对于所有客户端只有一个可用的实例,可以使用单例模式。
> - 如果你需要更加严格的控制全局变量,可以使用单例模式。
+ **单例模式优缺点:**
1.优点:
> - 你可以保证一个类只有一个实例。
> - 你获得了一个指向该实例的全局访问节点。
> - 仅在首次请求单例对象时对其进行初始化。
2.缺点:
> - 违反了 ***单一职责原则***。该模式同时解决了两个问题。
> - 单例模式可能掩盖不良设计,比如程序各组件之间相互了解过多等。
> - 该模式在多线程环境下需要进行特殊处理,避免多个线程多次创建单例对象。
> - 单例的客户端代码单元测试可能会比较困难,因为许多测试框架以基于继承的方式创建对象。由于单例类的构造函数时私有的,而且绝大部分语言无法重写静态方法,所以你需要想出仔细考虑模拟单例的方法。要么干脆不编写测试代码,或者不使用单例模式。
+ **与其他设计模式的关系:**
> - ***外观模式*** 类通常可以转换为 ***单例模式*** 类,因为在大部分情况下一个外观对象就够了。
> - 如果你能将对象的所有共享状态简化为一个享元对象,那么 ***享元模式*** 就和 ***单例*** 类似了。但这两各模式有个根本性的不同
1. 只会有一个单例实体,但是享元类可以有多个实体,各实体的内在状态也可以不同。
2. 单例对象可以是可变的。享元对象是不可变的。
> - ***抽象工厂模式*** 、 ***建造者模式*** 、和 ***原型模式*** 都可以用 ***单例*** 来实现。
+ **单例模式实现方式:**
1.静态类实现:
> - 这种静态类的方式可以在第一次运行的时候直接初始化。同时这里我们也不需要到延迟加载在使用。
> - 在不需要维持任何状态下,仅仅用于全局访问,这个是用静态类的方式更加方便。
> - 但如果需要被继承及需要维持一些特定状态的情况下,就是和使用单例模式。
2.懒汉模式(线程不安全):
> - 单例模式有一个特点就是不允许外部直接创建,也就是 new Singletin() ,因此这里在默认的构造函数上添加了私有属性 private.
> - 目前此种方式的单例确实满足了懒加载,但是如果有多个访问者同时去获取对象实例。可以想象成一堆人在抢厕所,就会造成多个同样的实例并存,从而没有达到单例的要求。
3.懒汉模式(线程安全):
> - 此种模式虽然是安全的,但由于把锁加载了方法上后,所有访问都因需要锁占用导致资源的浪费。如果不是特殊情况下,不建议使用此种方式实现单例模式
4.饿汉模式(线程安全):
> - 此种模式和静态类实现基本一致,在程序启动的时候直接运行加载,后续由外部需要使用的时候获取即可。
> - 但此种方式并不是懒加载,也就是说无论你程序中是否用到这样的类都会在程序启动之初进行创建。
> - 这种方式导致的问题就象你手机下载各游戏软件,可能你游戏地图还没打开呢,但是程序已经将这些地图全部实例化。到你手机最明显体验就已开游戏内存满了,手机卡了,需要换了。
5.使⽤类的内部类(线程安全):
> - 使用类的静态内部类实现单例模式,既保证了线程安全又保证了懒加载,同时不会因为枷锁的方式耗费性能。
> - 这主要是因为 JVM 虚拟机可以保证多线程并发访问的正确性,也就是一个类的构造方法在多线程环境下可以被正确的加载。
> - 此种方式也是非常推荐使用的一种单例模式。
6.双重锁校验(线程安全):
> - 双重锁的方式是方法级锁的优化,减少了部分获取实例的耗时。
> - 同时这种方式页满足了懒加载。
7.CAS「AtomicReference」(线程安全):
> - Java并发库提供了很多原子类来支持并访问的数据安全性:AtomicInteger、AtomicBoolean、AtomicLong、AtomicReference
> - AtomicReference 可以封装引用一个 V 实例,支持并发访问如上的单例模式就是使用了这样一个特点。
> - 使用 CAS 的好处就是不需要使用传统的加锁方式保证线程安全,而是依赖于 CAS 的忙等算法,依赖于底层硬件的实现,来保证线程安全。相对于其他所谁的实现没有线程的切面和阻塞也就没有了额外的开销,并可以支持较大的并发性。
> - 当然 CAS 也有一个缺点就是忙等,如果一直没有获取到将会处于死循环中。
8.Effective Java作者推荐的枚举单例(线程安全):
> - Effective Java 作者推荐使用枚举的方法解决单例模式,此种方式可能是平时最少用到的。
> - 这种方式解决了最主要的:线程安全、自由串行化、单一实例。
****
## 结构型模式(7种)
**定义:** 结构型模式介绍如何将对象和类组装成较大的结构,并同时保持结构的灵活和高效。
适配器模式 桥接模式 组合模式 装饰模式 外观模式 享元模式 代理模式
****
### 适配器模式(06)
**亦称:** 封装器模式、Wrapper、Adapter
+ **什么是适配器模式?**
> 适配器模式是一种结构型设计模式, 它能使接口不兼容的对象能够相互合作。
+ **应用场景:**
> - 当你希望使用某个类,但是接口与其他代码不兼容时,可以使用适配器
> - 如果你需要复用这样一个类,他们处于同一个继承体系,并且他们又有了额外的一些共同方法,但是这些共同的方法不是所有在这一继承体系中的子类所具有的共性
+ **适配器模式优缺点:**
1.优点:
> - ***单一职责原则***你可以将接口或数据转换代码从程序主要业务逻辑中分离。
> - 开闭原则。 只要客户端代码通过客户端接口与适配器进行交互, 你就能在不修改现有客户端代码的情况下在程序中添加新类型的适配器。
2.缺点:
> - 代码整体复杂度增加, 因为你需要新增一系列接口和类。 有时直接更改服务类使其与其他代码兼容会更简单。
+ **与其他设计模式的关系:**
> - ***桥接模式*** 通常会用于开发前期进行设计,使你能够将程序的各个部分独立开来以便开发。另一方面,***适配器***通常在已有程序中使用,让相互不兼容的类能很好的合作。
> - ***适配器*** 可以对已有对象的接口进行修改,***装饰模式*** 则能在不改变对象接口的前提下强化对象功能。此外, ***装饰模式*** 则能在不改变对象接口的前提下强化对象功能。此外,装饰还支持递归组合,适配器则无法实现。
> - ***适配器*** 能为被封装对象提供不同的接口,***代理模式*** 能为对象提供相同的接口,***装饰*** 则能为对象提供加强的接口。
> - ***外观模式*** 为现有对象定义了一个新接口,***适配器*** 则会试图运用已有的接口。***适配器*** 通常只封装一个对象,外观通常会作用与整个对象子系统上。
> - ***桥接*** 、 ***状态模式*** 、 和 ***策略模式*** (在某种程度上包括 ***适配器*** )模式的接口非常相似。实际上,他们都基于 ***组合模式*** ————即将工作委派给其他对象,不过也各自解决了不同的问题。模式并不只是以特定方式组织代码的配方,
你还可以使用它们来和其他开发者讨论模式所解决的问题。
****
### 桥接模式(07)
**亦称:** Bridge
+ **什么是桥接模式?**
> 适配器模式是一种结构性设计模式,可将一个大类或一系列紧密相关的类拆分为抽象和实现两个独立的层次结构,从而能在开发时分别使用。
> 简单来说核⼼实现也就是在A类中含有B类接⼝,通过构造函数传递B类的实现,这个B类就是设计的桥接
+ **应用场景:**
> - 如果你想要拆分或重组一个具有多重功能的庞杂类(例如能与多个数据库服务器进行交互的类),可以使用桥接模式。
> - 如果你希望在几个独立维度上扩展一个类,可使用该模式。
> - 如果你需要在运行时切换不同实现方法,可使用桥接模式。
+ **桥接模式优缺点:**
1.优点:
> - 你可以创建与平台无关的类和程序。
> - 客户端代码仅与高层抽象部分进行互动,不会接触到平台的详细信息
> - 开闭原则。你可以新增抽象部分和实现部分,且他们之间不会相互影响。
> - 单一职责原则。抽象部分专注于处理高层逻辑,实现部分处理平台细节。
2.缺点:
> - 对高内聚的类使用该模式可能会让代码更加复杂。
+ **与其他设计模式的关系:**
> - ***桥接模式*** 通常会用于开发前期进行设计,使你能够将程序的各个部分独立开来以便开发。另一方面,***适配器***通常在已有程序中使用,让相互不兼容的类能很好的合作。
> - ***桥接*** 、 ***状态模式*** 、 和 ***策略模式*** (在某种程度上包括 ***适配器*** )模式的接口非常相似。实际上,他们都基于 ***组合模式*** ————即将工作委派给其他对象,不过也各自解决了不同的问题。模式并不只是以特定方式组织代码的配方,
你还可以使用它们来和其他开发者讨论模式所解决的问题。
> - 你可以将***抽象工厂模式***和***桥接***搭配使用。如果桥接定义的抽象只能与特定实现合作,这一模式搭配就非常有用。在这种情况下,抽象工厂可以对这些关系进行封装,并且对客户端代码隐藏其复杂性。
> - 你可以结合使用***生成器模式***和***桥接模式***:主管类负责抽象工作,各种不同的生成器负责实现工作。
****
### 组合模式(08)
**亦称:** 对象树、Object Tree、Composite
+ **什么是组合模式?**
> 组合模式是一种结构性设计模式,你可以使用他将对象组合成树状结构,并且能像使用独立的对象一样使用它们
> 简单来说通过⼀堆的链接组织出⼀棵结构树。⽽这种通过把相似对象(也可以称作是⽅法)组合成⼀组可被调⽤的结构树对象的设计思路
+ **应用场景:**
> - 如果你希望实现树状对象结构,可以使用组合模式。
> - 如果你希望客户端代码以相同方式处理简单和复杂元素,可以使用该模式。
+ **组合模式优缺点:**
1.优点:
> - 你可以利用多态和递归机制更方便的使用复杂树结构。
> - 开闭原则,无需更改现有代码,你就可以在应用中添加新元素,使其成为对象树的一部分
2.缺点:
> - 对于功能差异较大的类,提供公共接口或许会有困难。在特定情况下,你需要过度一般化组件接口,使其变得令人难以理解。
+ **与其他设计模式的关系:**
> - ***桥接*** 、 ***状态模式*** 、 和 ***策略模式*** (在某种程度上包括 ***适配器*** )模式的接口非常相似。实际上,他们都基于 ***组合模式*** ————即将工作委派给其他对象,不过也各自解决了不同的问题。模式并不只是以特定方式组织代码的配方,
你还可以使用它们来和其他开发者讨论模式所解决的问题。
> - 你可以在创建复杂 ***组合*** 树时使用 ***建造者模式***,因为这可使其构造步骤以递归的方式运行。
> - ***责任链模式*** 通常和 ***组合模式*** 结合使用。在这种情况下,叶组件接收到请求后,可以将请求沿包含全体父组件的链一直传递至对象树的底部。
> - 你可以使用 ***迭代器模式*** 来遍历 ***组合*** 树。
> - 你可以使用 ***访问者模式*** 对整个 ***组合*** 树执行操作。
> - 你可以使用 ***享元模式*** 实现 ***组合*** 树的共享叶节点以节省内存。
> - ***组合*** 和 ***装饰模式*** 的结构图很相似,因为两者都依赖递归组合来组织无线数量的对象。
1. 装饰类似于组合,但其只有一个子组件。此外还有一个明显的不同:装饰为被封装对象添加了额外的职责,组合仅对其子节点的结果进行了求和
但是,模式也可以相互合作:你可以使用装饰来扩展组合树中特定对象的行为。
2. 大量使用 ***组合*** 和 ***装饰*** 的设计通常可从对于 ***原型模式*** 的使用中获益。你可以通过该模式来复制复杂结构,而非从零开始重新构造
****
### 装饰模式(09)
**亦称:** 装饰者模式、装饰器模式、Wrapper、Decorator
+ **什么是装饰模式?**
> 装饰模式是一种结构型设计模式,允许你通过将对象放入包含行为的特殊封装对象中委员有对象绑定新的行为
> 简单来说装饰器的核⼼就是再不改原有类的基础上,给类新增功能。
+ **应用场景:**
> - 若果你希望在无需修改代码的情况下即可使用对象,且希望在运行时为对象新增额外的行为,可以使用装饰器模式。
> - 如果用继承来扩展对象行为的方案难以实现或者根本不可行,你可以使用该模式。
+ **装饰模式优缺点:**
1.优点:
> - 你无需创建新子类即可扩展对象的行为。
> - 你可以在运行时添加或者删除对象的功能。
> - 你可以用多个装饰封装对象来组合几种行为。
> - 单一职责原则。你可以将实现了许多不同行为的一个大类拆分为多个较小的类。
2.缺点:
> - 在封装器栈中删除特定封装器比较困难。
> - 实现行为不受装饰栈顺序影响的装饰比较困难。
> - 各层的初始化配置代码看上去可能会很糟糕。
+ **与其他设计模式的关系:**
> - ***适配器模式*** 可以对已有对象的接口进行修改, ***装饰模式*** 则能在不改变对象接口的前提下强化对象功能。此外,装饰还支持递归组合,适配器无法实现。
> - ***适配器*** 能为被封装对象提供不同的接口, ***代理模式*** 能为对象提供相同的接口,***装饰*** 则能为对象提供加强的接口。
> - ***责任链模式*** 和 ***装饰模式*** 的类接口非常相似。 两者都依赖递归组合将需要执行的操作传递给一系列对象。但是,两者有几点重要的不同之处。
1. 责任链的管理者可以相互独立的执行一切操作,还可以随时停止传递请求。另一方面,各种装饰可以在遵循基本接口情况下扩展对象的行为。此外,装饰无法中断请求的传递。
> - ***组合模式*** 和 ***装饰*** 的结构图很相似,因为两者都依赖递归组合来组织无线数量的对象。
1.装饰模式类似于组合,但其只有一个子组件。此外还有一个明显不同:装饰为被封装对象添加了额外的职责,组合进对其子节点的结果进行了“求和”。
但是模式也可以相互合作:你可以使用装饰来扩展组合数中特定对象的行为。
> - 大量使用***组合*** 和 ***装饰*** 的设计通常可从对于 ***原型模式*** 的使用中获益。你可以通过该模式来复制复杂结构,而非从零开始重新构造。
> - ***装饰*** 可让你更改对象的外表,***策略模式*** 则让你能够改变其本质,
> - ***装饰*** 和 ***代理*** 有着相似的结构,但是其意图却非常不同。这两个模式的构建都基于组合原则,也就是说一个对象应该将部分工作委派给另一个对象。两者之间的不同指出在与代理通常自行管理
其服务对象的生命周期,而装饰的生成则总是由客户端进行过控制。
****
### 外观模式(10)
**亦称:** 门面模式、Facade
+ **什么是外观模式?**
> 外观模式是一种结构性设计模式,能为程序库、框架或其他复杂类提供一个简单的接口。
> 主要解决的是降低调⽤⽅的使⽤接⼝的复杂逻辑组合。这样调⽤⽅与实际的接⼝提供⽅提供⽅提供了⼀个中间层,⽤于包装逻辑提供API接⼝
+ **应用场景:**
> - 如果你需要一个指向复杂子系统的直接接口,且该接口的功能有限,则可以使用外观模式。
> - 如果用继承来扩展对象行为的方案难以实现或者根本不可行,你可以使用该模式。
+ **外观模式优缺点:**
1.优点:
> - 你可以让自己的代码独立于复杂子系统。
2.缺点:
> - 外观可能成为与程序中所有类都耦合的上帝对象
+ **与其他设计模式的关系:**
> - ***外观模式*** 为现有对象定义了一个新接口, ***适配器模式*** 则会视图运用已有的接口。适配器通常只封装一个对象,外观通常会作用与整个对象子系统上。
> - 当只需要对客户端代码隐藏子系统创建对象的方式时,你可以使用 ***抽象工厂模式*** 来代替 ***外观***
> - ***享元模式*** 展示了如和生成大量的小型对象,***外观*** 则展示了如何用一个对象来代表整个子系统。
> - ***外观*** 和 ***中介者模式*** 的职责类似:它们都尝试在大量紧密耦合的类中组织起合作。
1. 外观为子系统中所有对象定义了一个简单接口,但是他不提供任何新功能。子系统本身不会意识到外观的存在。子系统中的对象可以直接进行交流。
2. 中介者将系统种组件的沟通行为中心化。各组件只知道中介者对象,无法直接相互交流。
> - ***外观***类通常可以转换为 ***单例模式*** 类,因为在大部分情况下一个外观对象就够了。
> - ***外观*** 与 ***代理模式*** 的相似之处在于他们都缓存了一个复杂实体并自行对其进行初始化。代理与其服务对象遵循同一接口,使得自己和服务对象可以互换,在这一点上他与外观不同。
****
### 享元模式(11)
**亦称:** 缓存、Cache、Flyweight
+ **什么是享元模式?**
> 享元模式是一种结构性设计模式,它摒弃了在每个对象中保存所有数据的方式,通过共享多个对象所有的相同状态,让你能在有限的内容量中载入更多对象。
> 简单来说主要在于共享通用对象,减少内存的使用,提升系统的访问效率。而这部分共享对象通常比较耗费内存或者需要查询大量接口或者使用数据库资源,因此同一抽离作为共享对象使用。
+ **应用场景:**
> - 仅在程序必须支持大量对象且没有足够的内存容量时使用享元模式
+ **外观模式优缺点:**
1.优点:
> - 如果程序中有很多相似对象,那么你将可以节省大量内存。
2.缺点:
> - 你可能需要牺牲执行速度来换取内存,因为他人每次调用享元方法时都需要重新计算部分情景数据
> - 代码会变得更加复杂。团队中的新成员总是会问:“为什么要像这样拆分一个实体的状态?”
+ **与其他设计模式的关系:**
> - 你可以是使用***享元模式*** 实现 ***组合模式*** 树的共享叶子节点以节省内存。
> - ***享元*** 展示了如何生成大量的小型对象, ***外观模式*** 则展示了如何使用一个对象来代表整个子系统。
> - 如果你能将对象的所有共享状态简化成一个享元对象,那么***享元***就和***单例模式***类似了。但这两个模式有两个根本性不同。
1. 只会有一个单体实例,但是享元类可以有多个实体,个实体的内在状态也可以不同。
2. 单例对象是可以改变的,享元对象是不可变得。
****
### 代理模式(12)
**亦称:** Proxy
+ **什么是代理模式?**
> 代理模式是一种结构性设计模式,让你能够提供对象的替代品或其占位符。代理控制着对于原对象的访问,并允许在请求提交给对象前后进行一些处理。
> 简单来说代理模式有点像老大和小弟。也有点像分销商。主要解决的是问题是为某些资源的访问、对象的类的易用操作上提供方便使用的代理服务
+ **应用场景:**
> - 延迟初始化(虚拟代理)。如果你有一个偶尔使用的重量级服务对象,一直保持该对象运行会消耗系统资源时,可使用代理模式。
> - 访问控制(保护代理)。如果你只希望特定客户端使用服务对象,这里的对象可以是操作系统中非常重要的部分,而客户端则是各种已启动的程序(包括恶意程序),此时可以使用代理模式。
> - 本地执行远程服务(远程代理)。适用于服务对象位于远程服务器上的情形。
> - 记录日志请求(日志记录代理)。适用于当你需要保存对于服务对象的请求历史记录时。代理可以在像服务传递请求前进行记录。
> - 智能引用。可在没有客户端使用某个重量级对象时立即销毁该对象。
+ **代理模式优缺点:**
1.优点:
> - 你可以在客户端毫无察觉的情况下控制服务对象。
> - 如果客户端对服务对象的生命周期没有特殊要求。你可以对生命周期进行管理。
> - 即使服务对象还未准备好或不存在,代理也可以正常工作。
> - 开闭原则。你可以在不对服务或客户端做出修改的情况下创建新代理。
2.缺点:
> - 代码可能会变得复杂,因为需要新建许多类。
> - 服务响应可能会延迟。
+ **与其他设计模式的关系:**
> - ***适配器模式*** 能为被封装对象提供不同的接口, ***代理模式*** 能为对象提供相同的接口, ***装饰模式*** 则能为对象提供加强的接口。
> - ***外观模式*** 与 ***代理*** 的相似之处在于它们都缓存了一个复杂实体并自行对其进行初始化。代理与其服务对象遵循同一接口,使得自己和服务对象可以互换,在这一点上他与外观不同。
> - ***装饰*** 和 ***代理*** 有着相似的结构,但是其意图却非常不同。这两个模式的构建都基于组合原则,也就是说一个对象应该将部分工作委派给另一个对象。两者之间的不同之处在于代理
通常自行管理其服务对象的生命周期,而装饰的生成则总是有客户端进行控制。
****
## 行为模式(10种)
**定义:** 行为模式负责对象间的高效沟通和职责委派。
责任链模式 命令模式 迭代器模式 中介者模式 备忘录模式 观察者模式 状态模式 策略模式 模板方法模式 访问者模式
****
### 责任链模式(13)
**亦称:** 职责链模式、命令链、CoR、Chain of Command、Chain of Responsibility
+ **什么是代理模式?**
> 责任链模式是一种行为设计模式,允许你将请求沿着处理者链进行发送。收到请求后,每个处理者均可对请求进行处理。或将其传递给链上的下个处理者。
+ **应用场景:**
> - 当程序需要使用不同方式处理不同种类的请求,而且请求类型和顺序预先未知时,可以使用责任链模式。
> - 当必须按照顺序执行多个处理者时,可以使用该模式。
> - 如果所需处理者及其顺序必须在运行时进行改变,可以使用责任链模式。
+ **责任链模式优缺点:**
1.优点:
> - 你可以控制请求处理的顺序
> - 单一职责原则。你可以对发起操作和执行操作的类进行解耦
> - 开闭原则。你可以在不更改现有代码的情况下在程序中新增处理者
2.缺点:
> - 部分请求可能未被处理
+ **与其他设计模式的关系:**
> - ***责任链模式*** 、 ***命令模式*** 、 ***中介者模式*** 和 ***观察者模式*** 用于处理请求发送者和接收者之间不同连接方式:
1. 责任链按照顺序将请求动态传递给一系列的潜在接收者,直至其中一名接收者对请求进行处理。
2. 命令在发送者和请求者之间建立单向连接。
3. 中介者清除了发送者和请求者之间的直接连接。强制它们通过一个中介对象进行间接沟通
4. 观察者允许接收者动态地订阅或取消接受请求。
> - ***责任链*** 通常和 ***组合模式*** 结合使用。在这种情况下。叶组件接收到请求后,可以将请求沿包含全体父组件的链一直传递至对象树的底部。
> - ***责任链*** 的管理者可使用 ***命令模式*** 实现。在这种情况下,你可以对由请求代表的同一个上下文对象执行许多不同的操作。
1. 还有另外一种实现方式,那就是请求自身就是一个命令对象。在这种情况下,你可以对由一系列不同上下文连接而成的链执行相同的操作。
> - ***责任链*** 和 ***装饰模式***的类结构非常相似。两者都依赖递归组合将需要执行的操作传递给一系列对象。但是,两者有几点重要的不同之处。
1. ***责任链***的管理者可以相互独立的执行一切操作,还可以随时停止传递请求。另一方面,各种装饰可以在遵循基本接口的情况下扩展对象的行为。此外,装饰无法中断请求的传递。
****
### 命令模式(14)
**亦称:** 动作、事务、Action、Transaction、Command
+ **什么是命令模式?**
> 命令模式是一种行为设计模式。它可将请求与转换为一个包含请求相关的所有信息的独立对象。该转换让你根据不同的请求将方法参数化延迟请求执行、或将其放入队列中,且能实现可撤销操作。
> 通俗来讲:日常生活中常用的Ctrl + C、 Ctrl + V。从这样的操作感受上、可以想到这是把逻辑实现和操作请求进行分离,降低耦合方便扩展。
+ **应用场景:**
> - 如果你需要通过操作来参数化对象,可以使用命令模式。
> - 如果你想将操作放入队列中、操作的执行或者远程执行操作,可使用命令模式。
> - 如果你想要实现操作回滚功能,可使用命令模式。
+ **命令模式优缺点:**
1.优点:
> - 单一职责原则。你可以解耦触发和执行操作的类。
> - 开闭原则。你可以在不修改已有客户端代码的情况下在程序中创建新的命令。
> - 你可以实现撤销和恢复功能。
> - 你可以实现操作的延迟执行。
> - 你可以将一组简单命令组合成一个复杂命令。
2.缺点:
> - 代码可能会变得更加复杂,因为你在发送者和接收者之间增加可一个全新的层次
+ **与其他设计模式的关系:**
> - ***责任链模式*** 、 ***命令模式*** 、 ***中介者模式*** 和 ***观察者模式*** 用于处理请求发送者和接收者之间不同连接方式:
1. 责任链按照顺序将请求动态传递给一系列的潜在接收者,直至其中一名接收者对请求进行处理。
2. 命令在发送者和请求者之间建立单向连接。
3. 中介者清除了发送者和请求者之间的直接连接。强制它们通过一个中介对象进行间接沟通
4. 观察者允许接收者动态地订阅或取消接受请求。
> - ***责任链*** 的管理者可使用 ***命令模式*** 实现。在这种情况下,你可以对由请求代表的同一个上下文对象执行许多不同的操作。
1. 还有另外一种实现方式,那就是请求自身就是一个命令对象。在这种情况下,你可以对由一系列不同上下文连接而成的链执行相同的操作。
2. 你可以同时使用 ***命令*** 和 ***备忘录模式*** 来实现“撤销”。在这种情况下,命令用于对目标对象执行各种不同的操作,备忘录用来保存一条命令执行前该对象的状态。
> - ***命令*** 和 ***策略模式*** 看上去很像,因为两者都能通过某些行为来参数化对象。但是,他们的意图有非常大的不同。
1.你可以使用命令来将任何操作转化为对象。操作的参数将成为对象的成员变量。你可以通过转换来延迟操作的执行、将操作放入队列、保存历史命令或者向远程服务器发送命令等。
2.另一方面,***策略*** 通常可用于描述完成某件事的不同方式,让你能够在同一个上下文类中切换算法。
> - ***原型模式*** 可用于保存 ***命令*** 的历史记录。
> - 你可以将 ***访问者模式*** 视为 ***命令模式*** 的加强版本,其对象可对不同类的多种对象执行操作。
****
### 迭代器模式(15)
**亦称:** iterator
+ **什么是迭代器模式?**
> 迭代器模式是一种行为设计模式。让你能在不暴露集合底层表现形式(列表、栈和树等)的情况下遍历集合中所有的元素。
+ **应用场景:**
> - 当集合背后为复杂的数据结构,且你希望对客户端隐藏其复杂性时(出于使用便利性或安全性的考虑),可以使用迭代器模式。
> - 使用该模式可以减少程序中重复的遍历代码。
> - 如果你希望代码能够遍历不同的甚至是无法预知的数据结构,可以使用迭代器模式
+ **命令模式优缺点:**
1.优点:
> - 单一职责原则。通过将体积庞大的遍历算法代码抽取为独立的类,你可对客户端代码和集合进行整理。
> - 开闭原则。你可实现新型的集合和迭代器并将其传递给现有代码,无需修改现有代码。
> - 你可以并行遍历同一集合,因为每个迭代器对象都包含其自身的遍历状态。
> - 相似的,你可以暂停遍历并在需要时继续
2.缺点:
> - 如果你的程序只与简单的集合进行交互。应用该模式可能会矫枉过正。
> - 对于某些特殊集合,使用迭代器可能比直接遍历的效率低。
+ **与其他设计模式的关系:**
> - 你可以使用 ***迭代器模式*** 来遍历 ***组合模式***树
> - 你可以同时使用 ***工厂方法模式*** 和 ***迭代器*** 来让子类集合返回不同类型的迭代器, 并使得迭代器与集合相匹配。
> - 你可以同时使用 ***备忘录模式*** 和 ***迭代器*** 来获取当前迭代器的状态,并且在需要的时候进行回滚。
> - 可以同时使用 ***访问者模式*** 和 ***迭代器*** 来遍历复杂数据结构,并对其中的元素执行所需操作,即使这些元素所属的类完全不同。
****
### 中介者模式(16)
**亦称:** 调解人、控制器、Intermediary、Controller、Mediator
+ **什么是中介者模式?**
> 中介者模式是一种行为设计模式。能让你减少对象之间混乱无序的依赖关系。该模式会限制对象之间的直接交互。迫使他们通过一个中介者对象进行合作。
+ **应用场景:**
> - 当一些对象和其他对象紧密耦合以致难以对其进行修改时,可使用中介者模式。
> - 当组件因过于依赖于其他组件而无法在不同应用中复用时,可使用中介者模式。
> - 如果为了能在不同情境下复用一些基本行为,导致你需要被迫创建大量组件子类时,可使用中介者模式。
+ **中介者模式优缺点:**
1.优点:
> - 单一职责原则。你可以将多个组件间的交流抽取到同一位置,使其更易于理解和维护。
> - 开闭原则。你无需修改实际组件就能增加新的中介者。
> - 你可以减轻应用中多个组件间的耦合情况。
> - 你可以更方便的复用各个组件
2.缺点:
> - 一段时间后,中介者可能会演化为 ***上帝对象***
+ **与其他设计模式的关系:**
> - ***责任链模式*** 、 ***命令模式*** 、 ***中介者模式*** 和 ***观察者模式*** 用于处理请求发送者和接收者之间的不同连接方式:
1.责任链按照舒徐将请求动态传递给一系列的潜在接收者。直至其中一名接收者对请求进行处理。
2.命令在发送者和请求者之间建立单项连接。
3.中介者消除了发送者和请求者之间的直接连接,强制他们通过一个中介对象进行间接沟通。
4.观察者允许接收者动态地订阅或取消接收请求。
> - ***外观模式*** 和 ***中介者*** 的职责类似:他们都尝试在大量紧密耦合的类中组织起合作。
1.外观为子系统中的所有对象定义了一个简单接口,但是他不提供任何新功能。子系统本身不会意识到外观的存在。子系统中的对象可以直接进行交流。
2.中介者将系统中的组件的沟通行为中心化。各组件只知道中介者对象,无法直接进行交流。
> - ***中介者*** 和 ***观察者*** 之间的区别往往很难记住,在大部分情况下,你可以使用其中一种模式,而有时间可以同时使用。让我们来看看如何做到这一点。
1.中介者的主要目的是消除一系列系统组件之间的相互依赖。这些组件将依赖于同一个中介者对象。观察者的目标是在对象之间建立动态的单项连接,使得部分对象可作为其他对象的附属发挥作用
2.有一种流行的中介者模式实现方式依赖于观察者。中介者对象担当发布者的角色,其他组件则视为订阅者,可以订阅中介者的事件或取消订阅。当中介者以这种方式实现时,它可能看上去与观察者非常相似。
3.当你感到疑惑时,记住可以采用其他方式来实现中介者。例如,你可永久性地将所有组件链接到同一个中介者对象。这种实现方式和观察者并不相同,但这仍是一种中介者模式。
4.假设有一个程序,其所有的组件都变成了发布者,他们之间可以相互建立动态连接。这样程序中就没有中心化的中介者对象,而只有一些分布式的观察者。
****
### 备忘录模式(17)
**亦称:** 快照、Snapshot、Memento
+ **什么是备忘录模式?**
> 备忘录模式是一种行为设计模式。允许在不暴露对象的实现细节的情况下保存和回复对象之前的状态。
> 简单来说,备忘率模式是以可以恢复或者说回滚,配置、版本、悔棋为核心功能的设计模式。
+ **应用场景:**
> - 当你需要创建对象状态快照来恢复其之前的状态时,可以使用备忘录模式。
> - 当直接访问对象的成员变量、获取器或者设置器将导致封装被突破时,可以使用该模式。
+ **中介者模式优缺点:**
1.优点:
> - 你可以在不破坏对象封装情况的前提下创建对象状态快照
> - 你可以通过让负责人维护原发器状态历史记录来简化原发器代码
2.缺点:
> - 如果客户端过于频繁的创建备忘录,程序将消耗大量内存。
> - 负责人必须完整跟踪原发器的生命周期,这样才能销毁弃用的备忘录。
> - 绝大部分动态编程语言(例如PHP、Python 和 JavaScript)不能确保备忘录中的状态不被修改
+ **与其他设计模式的关系:**
> - 你可以同时使用 ***命令模式*** 和 ***备忘录模式*** 来实现“撤销”。在这种情况下,命令用于对目标对象执行各种不同操作,备忘录用来报错一条命令执行前该对象的状态。
> - 你可以同时使用 ***备忘录模式*** 和 ***迭代器模式*** 来获取当前迭代器的状态,并且在需要的时候进行回滚。
> - 有时候 ***原型模式*** 可以作为 ***备忘录模式*** 的一个简化版本,其条件是你需要在历史记录中存储对象的状态比较简单,不需要链接其他外部资源,或者链接可以方便的重建。
****
## 参考资料
> [【重学 Java 设计模式】](https://bugstack.cn/itstack/itstack-demo-design.html)
> [【菜鸟教程-设计模式】](https://www.runoob.com/design-pattern/design-pattern-tutorial.html)
> [【 国外图解设计模式网站】](https://refactoringguru.cn/)