auto commit

This commit is contained in:
CyC2018
2018-09-01 01:22:24 +08:00
parent 06190f2f19
commit 0b2d8ac6fd
16 changed files with 263 additions and 302 deletions

View File

@ -44,11 +44,11 @@
## 1. 单例Singleton
### 意图
### Intent
确保一个类只有一个实例,并提供该实例的全局访问点。
### 类图
### Class Diagram
使用一个私有构造函数、一个私有静态变量以及一个公有静态函数来实现。
@ -56,13 +56,13 @@
<div align="center"> <img src="../pics//562f2844-d77c-40e0-887a-28a7128abd42.png"/> </div><br>
### 实现
### Implementation
(一)懒汉式-线程不安全
#### 懒汉式-线程不安全
以下实现中,私有静态变量 uniqueInstance 被延迟实例化,这样做的好处是,如果没有用到该类,那么就不会实例化 uniqueInstance从而节约资源。
这个实现在多线程环境下是不安全的,如果多个线程能够同时进入 `if (uniqueInstance == null)` ,并且此时 uniqueInstance 为 null那么会有多个线程执行 `uniqueInstance = new Singleton();` 语句,这将导致多次实例化 uniqueInstance。
这个实现在多线程环境下是不安全的,如果多个线程能够同时进入 `if (uniqueInstance == null)` ,并且此时 uniqueInstance 为 null那么会有多个线程执行 `uniqueInstance = new Singleton();` 语句,这将导致实例化多次 uniqueInstance。
```java
public class Singleton {
@ -81,9 +81,9 @@ public class Singleton {
}
```
(二)饿汉式-线程安全
#### Ⅱ 饿汉式-线程安全
线程不安全问题主要是由于 uniqueInstance 被多次实例化,采取直接实例化 uniqueInstance 的方式就不会产生线程不安全问题。
线程不安全问题主要是由于 uniqueInstance 被实例化多次,采取直接实例化 uniqueInstance 的方式就不会产生线程不安全问题。
但是直接实例化的方式也丢失了延迟实例化带来的节约资源的好处。
@ -91,11 +91,11 @@ public class Singleton {
private static Singleton uniqueInstance = new Singleton();
```
(三)懒汉式-线程安全
#### Ⅲ 懒汉式-线程安全
只需要对 getUniqueInstance() 方法加锁,那么在一个时间点只能有一个线程能够进入该方法,从而避免了多次实例化 uniqueInstance 的问题
只需要对 getUniqueInstance() 方法加锁,那么在一个时间点只能有一个线程能够进入该方法,从而避免了实例化多次 uniqueInstance。
但是当一个线程进入该方法之后,其它试图进入该方法的线程都必须等待,因此性能上有一定的损耗
但是当一个线程进入该方法之后,其它试图进入该方法的线程都必须等待,即使 uniqueInstance 已经被实例化了。这会让线程阻塞时间过程,因此该方法有性能问题,不推荐使用
```java
public static synchronized Singleton getUniqueInstance() {
@ -106,7 +106,7 @@ public static synchronized Singleton getUniqueInstance() {
}
```
(四)双重校验锁-线程安全
#### Ⅳ 双重校验锁-线程安全
uniqueInstance 只需要被实例化一次,之后就可以直接使用了。加锁操作只需要对实例化那部分的代码进行,只有当 uniqueInstance 没有被实例化时,才需要进行加锁。
@ -133,7 +133,7 @@ public class Singleton {
}
```
考虑下面的实现,也就是只使用了一个 if 语句。在 uniqueInstance == null 的情况下,如果两个线程同时执行 if 语句,那么两个线程就会同时进入 if 语句块内。虽然在 if 语句块内有加锁操作,但是两个线程都会执行 `uniqueInstance = new Singleton();` 这条语句,只是先后的问题,那么就会进行两次实例化,从而产生了两个实例。因此必须使用双重校验锁,也就是需要使用两个 if 语句。
考虑下面的实现,也就是只使用了一个 if 语句。在 uniqueInstance == null 的情况下,如果两个线程执行 if 语句,那么两个线程都会进入 if 语句块内。虽然在 if 语句块内有加锁操作,但是两个线程都会执行 `uniqueInstance = new Singleton();` 这条语句,只是先后的问题,那么就会进行两次实例化。因此必须使用双重校验锁,也就是需要使用两个 if 语句。
```java
if (uniqueInstance == null) {
@ -145,19 +145,19 @@ if (uniqueInstance == null) {
uniqueInstance 采用 volatile 关键字修饰也是很有必要的。`uniqueInstance = new Singleton();` 这段代码其实是分为三步执行。
1. 分配内存空间
2. 初始化对象
1. 为 uniqueInstance 分配内存空间
2. 初始化 uniqueInstance
3. 将 uniqueInstance 指向分配的内存地址
但是由于 JVM 具有指令重排的特性,有可能执行顺序变为了 1>3>2,这在单线程情况下自然是没有问题。但如果是多线程下,有可能获得是一个还没有初始化的实例,以致于程序出错
但是由于 JVM 具有指令重排的特性,执行顺序有可能变成 1>3>2。指令重排在单线程环境下不会出先问题,但是在多线程环境下会导致一个线程获得还没有初始化的实例。例如,线程 T<sub>1</sub> 执行了 1 和 3此时 T<sub>2</sub> 调用 getUniqueInstance() 后发现 uniqueInstance 不为空,因此返回 uniqueInstance但此时 uniqueInstance 还未被初始化
使用 volatile 可以禁止 JVM 的指令重排,保证在多线程环境下也能正常运行。
(五)静态内部类实现
#### 静态内部类实现
当 Singleton 类加载时,静态内部类 SingletonHolder 没有被加载进内存。只有当调用 `getUniqueInstance()` 方法从而触发 `SingletonHolder.INSTANCE` 时 SingletonHolder 才会被加载,此时初始化 INSTANCE 实例。
当 Singleton 类加载时,静态内部类 SingletonHolder 没有被加载进内存。只有当调用 `getUniqueInstance()` 方法从而触发 `SingletonHolder.INSTANCE` 时 SingletonHolder 才会被加载,此时初始化 INSTANCE 实例,并且 JVM 能确保 INSTANCE 只被实例化一次
这种方式不仅具有延迟初始化的好处,而且由虚拟机提供了对线程安全的支持。
这种方式不仅具有延迟初始化的好处,而且由 JVM 提供了对线程安全的支持。
```java
public class Singleton {
@ -175,40 +175,11 @@ public class Singleton {
}
```
(六)枚举实现
该实现在多次序列化再进行反序列化之后,不会得到多个实例。而其它实现,为了保证不会出现反序列化之后出现多个实例,需要使用 transient 修饰所有字段,并且实现序列化和反序列化的方法。
这是单例模式的最佳实践,它实现简单,并且在面对复杂的序列化或者反射攻击的时候,能够防止实例化多次
该实现可以防止反射攻击。在其它实现中,通过 setAccessible() 方法可以将私有构造函数的访问级别设置为 public然后调用构造函数从而实例化对象如果要防止这种攻击需要在构造函数中添加防止实例化第二个对象的代码。但是该实现是由 JVM 保证只会实例化一次,因此不会出现上述的反射攻击
```java
public enum Singleton {
uniqueInstance;
}
```
考虑以下单例模式的实现,该 Singleton 在每次序列化的时候都会创建一个新的实例,为了保证只创建一个实例,必须声明所有字段都是 transient并且提供一个 readResolve() 方法。
```java
public class Singleton implements Serializable {
private static Singleton uniqueInstance;
private Singleton() {
}
public static synchronized Singleton getUniqueInstance() {
if (uniqueInstance == null) {
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
}
```
如果不使用枚举来实现单例模式,会出现反射攻击,因为通过 setAccessible() 方法可以将私有构造函数的访问级别设置为 public然后调用构造函数从而实例化对象。如果要防止这种攻击需要在构造函数中添加防止实例化第二个对象的代码。
从上面的讨论可以看出,解决序列化和反射攻击很麻烦,而枚举实现不会出现这两种问题,所以说枚举实现单例模式是最佳实践。
### 使用场景
### Examples
- Logger Classes
- Configuration Classes
@ -223,11 +194,11 @@ public class Singleton implements Serializable {
## 2. 简单工厂Simple Factory
### 意图
### Intent
在创建一个对象时不向客户暴露内部细节,并提供一个创建对象的通用接口。
### 类图
### Class Diagram
简单工厂不是设计模式,更像是一种编程习惯。它把实例化的操作单独放到一个类中,这个类就成为简单工厂类,让简单工厂类来决定应该用哪个具体子类来实例化。
@ -235,7 +206,7 @@ public class Singleton implements Serializable {
<div align="center"> <img src="../pics//c79da808-0f28-4a36-bc04-33ccc5b83c13.png"/> </div><br>
### 实现
### Implementation
```java
public interface Product {
@ -303,11 +274,11 @@ public class Client {
## 3. 工厂方法Factory Method
### 意图
### Intent
定义了一个创建对象的接口,但由子类决定要实例化哪个类。工厂方法把实例化操作推迟到子类。
### 类图
### Class Diagram
在简单工厂中,创建对象的是另一个类,而在工厂方法中,是由子类来创建对象。
@ -315,7 +286,7 @@ public class Client {
<div align="center"> <img src="../pics//1818e141-8700-4026-99f7-900a545875f5.png"/> </div><br>
### 实现
### Implementation
```java
public abstract class Factory {
@ -363,11 +334,11 @@ public class ConcreteFactory2 extends Factory {
## 4. 抽象工厂Abstract Factory
### 意图
### Intent
提供一个接口,用于创建 **相关的对象家族**
### 类图
### Class Diagram
抽象工厂模式创建的是对象家族,也就是很多对象而不是一个对象,并且这些对象是相关的,也就是说必须一起创建出来。而工厂方法模式只是用于创建一个对象,这和抽象工厂模式有很大不同。
@ -379,7 +350,7 @@ public class ConcreteFactory2 extends Factory {
<div align="center"> <img src="../pics//8668a3e1-c9c7-4fcb-98b2-a96a5d841579.png"/> </div><br>
### 代码实现
### Implementation
```java
public class AbstractProductA {
@ -461,15 +432,15 @@ public class Client {
## 5. 生成器Builder
### 意图
### Intent
封装一个对象的构造过程,并允许按步骤构造。
### 类图
### Class Diagram
<div align="center"> <img src="../pics//13b0940e-d1d7-4b17-af4f-b70cb0a75e08.png"/> </div><br>
### 实现
### Implementation
以下是一个简易的 StringBuilder 实现,参考了 JDK 1.8 源码。
@ -551,15 +522,15 @@ abcdefghijklmnopqrstuvwxyz
## 6. 原型模式Prototype
### 意图
### Intent
使用原型实例指定要创建对象的类型,通过复制这个原型来创建新对象。
### 类图
### Class Diagram
<div align="center"> <img src="../pics//a40661e4-1a71-46d2-a158-ff36f7fc3331.png"/> </div><br>
### 实现
### Implementation
```java
public abstract class Prototype {
@ -610,17 +581,17 @@ abc
## 1. 责任链Chain Of Responsibility
### 意图
### Intent
使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链发送该请求,直到有一个对象处理它为止。
### 类图
### Class Diagram
- Handler定义处理请求的接口并且实现后继链successor
<div align="center"> <img src="../pics//691f11eb-31a7-46be-9de1-61f433c4b3c7.png"/> </div><br>
### 实现
### Implementation
```java
public abstract class Handler {
@ -724,11 +695,16 @@ request2 is handle by ConcreteHandler2
## 2. 命令Command
### 意图
### Intent
将命令封装成对象中,以便使用命令来参数化其它对象,或者将命令对象放入队列中进行排队,或者将命令对象的操作记录到日志中,以及支持可撤销的操作。
将命令封装成对象中,具有以下作用:
### 类图
- 使用命令来参数化其它对象
- 将命令放入队列中进行排队
- 将命令的操作记录到日志中
- 支持可撤销的操作
### Class Diagram
- Command命令
- Receiver命令接收者也就是命令真正的执行者
@ -737,7 +713,7 @@ request2 is handle by ConcreteHandler2
<div align="center"> <img src="../pics//ae1b27b8-bc13-42e7-ac12-a2242e125499.png"/> </div><br>
### 实现
### Implementation
设计一个遥控器,可以控制电灯开关。
@ -847,18 +823,18 @@ public class Client {
## 3. 解释器Interpreter
### 意图
### Intent
为语言创建解释器,通常由语言的语法和语法分析来定义。
### 类图
### Class Diagram
- TerminalExpression终结符表达式每个终结符都需要一个 TerminalExpression
- Context上下文包含解释器之外的一些全局信息
- TerminalExpression终结符表达式每个终结符都需要一个 TerminalExpression
- Context上下文包含解释器之外的一些全局信息
<div align="center"> <img src="../pics//794239e3-4baf-4aad-92df-f02f59b2a6fe.png"/> </div><br>
### 实现
### Implementation
以下是一个规则检验器实现,具有 and 和 or 规则,通过规则可以构建一颗解析树,用来检验一个文本是否满足解析树定义的规则。
@ -971,11 +947,11 @@ false
## 4. 迭代器Iterator
### 意图
### Intent
提供一种顺序访问聚合对象元素的方法,并且不暴露聚合对象的内部表示。
### 类图
### Class Diagram
- Aggregate 是聚合类,其中 createIterator() 方法可以产生一个 Iterator
- Iterator 主要定义了 hasNext() 和 next() 方法。
@ -983,7 +959,7 @@ false
<div align="center"> <img src="../pics//b0f61ac2-a4b6-4042-9cf0-ccf4238c1ff7.png"/> </div><br>
### 实现
### Implementation
```java
public interface Aggregate {
@ -1059,18 +1035,18 @@ public class Client {
## 5. 中介者Mediator
### 意图
### Intent
集中相关对象之间复杂的沟通和控制方式。
### 类图
### Class Diagram
- Mediator中介者定义一个接口用于与各同事Colleague对象通信。
- Colleague同事相关对象
<div align="center"> <img src="../pics//d0afdd23-c9a5-4d1c-9b3d-404bff3bd0d1.png"/> </div><br>
### 实现
### Implementation
Alarm闹钟、CoffeePot咖啡壶、Calendar日历、Sprinkler喷头是一组相关的对象在某个对象的事件产生时需要去操作其它对象形成了下面这种依赖结构
@ -1228,11 +1204,11 @@ doSprinkler()
## 6. 备忘录Memento
### 意图
### Intent
在不违反封装的情况下获得对象的内部状态,从而在需要时可以将对象恢复到最初状态。
### 类图
### Class Diagram
- Originator原始对象
- Caretaker负责保存好备忘录
@ -1240,7 +1216,7 @@ doSprinkler()
<div align="center"> <img src="../pics//867e93eb-3161-4f39-b2d2-c0cd3788e194.png"/> </div><br>
### 实现
### Implementation
以下实现了一个简单计算器程序,可以输入两个值,然后计算这两个值的和。备忘录模式允许将这两个值存储起来,然后在某个时刻用存储的状态进行恢复。
@ -1405,7 +1381,7 @@ public class Client {
## 7. 观察者Observer
### 意图
### Intent
定义对象之间的一对多依赖,当一个对象状态改变时,它的所有依赖都会收到通知并且自动更新状态。
@ -1413,7 +1389,7 @@ public class Client {
<div align="center"> <img src="../pics//7a3c6a30-c735-4edb-8115-337288a4f0f2.jpg" width="600"/> </div><br>
### 类图
### Class Diagram
主题Subject具有注册和移除观察者、并通知所有观察者的功能主题是通过维护一张观察者列表来实现这些操作的。
@ -1421,7 +1397,7 @@ public class Client {
<div align="center"> <img src="../pics//0df5d84c-e7ca-4e3a-a688-bb8e68894467.png"/> </div><br>
### 实现
### Implementation
天气数据布告板会在天气信息发生改变时更新其内容,布告板有多个,并且在将来会继续增加。
@ -1540,15 +1516,15 @@ StatisticsDisplay.update: 1.0 1.0 1.0
## 8. 状态State
### 意图
### Intent
允许对象在内部状态改变时改变它的行为,对象看起来好像修改了它所属的类。
### 类图
### Class Diagram
<div align="center"> <img src="../pics//c5085437-54df-4304-b62d-44b961711ba7.png"/> </div><br>
### 实现
### Implementation
糖果销售机有多种状态,每种状态下销售机有不同的行为,状态可以发生转移,使得销售机的行为也发生改变。
@ -1842,16 +1818,16 @@ No gumball dispensed
## 9. 策略Strategy
### 意图
### Intent
定义一系列算法,封装每个算法,并使它们可以互换。
策略模式可以让算法独立于使用它的客户端。
### 类图
### Class Diagram
- Strategy 接口定义了一个算法族,它们都具有 behavior() 方法。
- Context 是使用到该算法族的类,其中的 doSomething() 方法会调用 behavior()setStrategy(in Strategy) 方法可以动态地改变 strategy 对象,也就是说能动态地改变 Context 所使用的算法。
- Context 是使用到该算法族的类,其中的 doSomething() 方法会调用 behavior()setStrategy(Strategy) 方法可以动态地改变 strategy 对象,也就是说能动态地改变 Context 所使用的算法。
<div align="center"> <img src="../pics//1fc969e4-0e7c-441b-b53c-01950d2f2be5.png"/> </div><br>
@ -1861,7 +1837,7 @@ No gumball dispensed
状态模式主要是用来解决状态转移的问题,当状态发生转移了,那么 Context 对象就会改变它的行为;而策略模式主要是用来封装一组可以互相替代的算法族,并且可以根据需要动态地去替换 Context 使用的算法。
### 实现
### Implementation
设计一个鸭子,它可以动态地改变叫声。这里的算法族是鸭子的叫声行为。
@ -1930,17 +1906,17 @@ quack!
## 10. 模板方法Template Method
### 意图
### Intent
定义算法框架,并将一些步骤的实现延迟到子类。
通过模板方法,子类可以重新定义算法的某些步骤,而不用改变算法的结构。
### 类图
### Class Diagram
<div align="center"> <img src="../pics//c3c1c0e8-3a78-4426-961f-b46dd0879dd8.png"/> </div><br>
### 实现
### Implementation
冲咖啡和冲茶都有类似的流程,但是某些步骤会有点不一样,要求复用那些相同步骤的代码。
@ -2031,11 +2007,11 @@ Tea.addCondiments
## 11. 访问者Visitor
### 意图
### Intent
为一个对象结构(比如组合结构)增加新能力。
### 类图
### Class Diagram
- Visitor访问者为每一个 ConcreteElement 声明一个 visit 操作
- ConcreteVisitor具体访问者存储遍历过程中的累计结果
@ -2043,7 +2019,7 @@ Tea.addCondiments
<div align="center"> <img src="../pics//ec923dc7-864c-47b0-a411-1f2c48d084de.png"/> </div><br>
### 实现
### Implementation
```java
public interface Element {
@ -2238,17 +2214,17 @@ Number of items: 6
## 12. 空对象Null
### 意图
### Intent
使用什么都不做的空对象来代替 NULL。
一个方法返回 NULL意味着方法的调用端需要去检查返回值是否是 NULL这么做会导致非常多的冗余的检查代码。并且如果某一个调用端忘记了做这个检查返回值而直接使用返回的对象那么就有可能抛出空指针异常。
### 类图
### Class Diagram
<div align="center"> <img src="../pics//dd3b289c-d90e-44a6-a44c-4880517eb1de.png"/> </div><br>
### 实现
### Implementation
```java
public abstract class AbstractOperation {
@ -2294,17 +2270,17 @@ public class Client {
## 1. 适配器Adapter
### 意图
### Intent
把一个类接口转换成另一个用户需要的接口。
<div align="center"> <img src="../pics//3d5b828e-5c4d-48d8-a440-281e4a8e1c92.png"/> </div><br>
### 类图
### Class Diagram
<div align="center"> <img src="../pics//0f754c1d-b5cb-48cd-90e0-4a86034290a1.png"/> </div><br>
### 实现
### Implementation
鸭子Duck和火鸡Turkey拥有不同的叫声Duck 的叫声调用 quack() 方法,而 Turkey 调用 gobble() 方法。
@ -2365,18 +2341,18 @@ public class Client {
## 2. 桥接Bridge
### 意图
### Intent
将抽象与实现分离开来,使它们可以独立变化。
### 类图
### Class Diagram
- Abstraction定义抽象类的接口
- Implementor定义实现类接口
<div align="center"> <img src="../pics//c2cbf5d2-82af-4c78-bd43-495da5adf55f.png"/> </div><br>
### 实现
### Implementation
RemoteControl 表示遥控器,指代 Abstraction。
@ -2518,11 +2494,11 @@ public class Client {
## 3. 组合Composite
### 意图
### Intent
将对象组合成树形结构来表示“整体/部分”层次关系,允许用户以相同的方式处理单独对象和组合对象。
### 类图
### Class Diagram
组件Component类是组合类Composite和叶子类Leaf的父类可以把组合类看成是树的中间节点。
@ -2530,7 +2506,7 @@ public class Client {
<div align="center"> <img src="../pics//3fb5b255-b791-45b6-8754-325c8741855a.png"/> </div><br>
### 实现
### Implementation
```java
public abstract class Component {
@ -2652,17 +2628,17 @@ Composite:root
## 4. 装饰Decorator
### 意图
### Intent
为对象动态添加功能。
### 类图
### Class Diagram
装饰者Decorator和具体组件ConcreteComponent都继承自组件Component具体组件的方法实现不需要依赖于其它对象而装饰者组合了一个组件这样它可以装饰其它装饰者或者具体组件。所谓装饰就是把这个装饰者套在被装饰者之上从而动态扩展被装饰者的功能。装饰者的方法有一部分是自己的这属于它的功能然后调用被装饰者的方法实现从而也保留了被装饰者的功能。可以看到具体组件应当是装饰层次的最低层因为只有具体组件的方法实现不需要依赖于其它对象。
<div align="center"> <img src="../pics//137c593d-0a9e-47b8-a9e6-b71f540b82dd.png"/> </div><br>
### 实现
### Implementation
设计不同种类的饮料,饮料可以添加配料,比如可以添加牛奶,并且支持动态添加新配料。每增加一种配料,该饮料的价格就会增加,要求计算一种饮料的价格。
@ -2759,15 +2735,15 @@ public class Client {
## 5. 外观Facade
### 意图
### Intent
提供了一个统一的接口,用来访问子系统中的一群接口,从而让子系统更容易使用。
### 类图
### Class Diagram
<div align="center"> <img src="../pics//f9978fa6-9f49-4a0f-8540-02d269ac448f.png"/> </div><br>
### 实现
### Implementation
观看电影需要操作很多电器,使用外观模式实现一键看电影功能。
@ -2814,11 +2790,11 @@ public class Client {
## 6. 享元Flyweight
### 意图
### Intent
利用共享的方式来支持大量细粒度的对象,这些对象一部分内部状态是相同的。
### 类图
### Class Diagram
- Flyweight享元对象
- IntrinsicState内部状态享元对象共享内部状态
@ -2826,7 +2802,7 @@ public class Client {
<div align="center"> <img src="../pics//d52270b4-9097-4667-9f18-f405fc661c99.png"/> </div><br>
### 实现
### Implementation
```java
public interface Flyweight {
@ -2899,11 +2875,11 @@ Java 利用缓存来加速大量小对象的访问时间。
## 7. 代理Proxy
### 意图
### Intent
控制对其它对象的访问。
### 类图
### Class Diagram
代理有以下四类:
@ -2914,7 +2890,7 @@ Java 利用缓存来加速大量小对象的访问时间。
<div align="center"> <img src="../pics//a6c20f60-5eba-427d-9413-352ada4b40fe.png"/> </div><br>
### 实现
### Implementation
以下是一个虚拟代理的实现,模拟了图片延迟加载的情况下使用与图片大小相等的临时内容去替换原始图片,直到图片加载完成才将图片显示出来。