diff --git a/notes/设计模式.md b/notes/设计模式.md
index 1a12eb41..47195c72 100644
--- a/notes/设计模式.md
+++ b/notes/设计模式.md
@@ -1,75 +1,77 @@
-* [前言](#前言)
-* [设计模式入门](#设计模式入门)
-* [观察者模式](#观察者模式)
-* [装饰模式](#装饰模式)
-* [工厂模式](#工厂模式)
-* [单件模式](#单件模式)
-* [命令模式](#命令模式)
-* [适配器模式](#适配器模式)
-* [外观模式](#外观模式)
-* [模板方法模式](#模板方法模式)
-* [迭代器模式](#迭代器模式)
-* [组合模式](#组合模式)
-* [状态模式](#状态模式)
-* [代理模式](#代理模式)
-* [复合模式](#复合模式)
-* [与设计模式相处](#与设计模式相处)
-* [剩下的模式](#剩下的模式)
+* [一、前言](#一前言)
+* [二、设计模式概念](#二设计模式概念)
+* [三、策略模式](#三策略模式)
+* [三、观察者模式](#三观察者模式)
+* [四、装饰模式](#四装饰模式)
+* [五、简单工厂](#五简单工厂)
+* [六、工厂方法模式](#六工厂方法模式)
+* [七、抽象工厂模式](#七抽象工厂模式)
+* [八、单例模式](#八单例模式)
+* [九、命令模式](#九命令模式)
+* [十、适配器模式](#十适配器模式)
+* [十、外观模式](#十外观模式)
+* [十一、模板方法模式](#十一模板方法模式)
+* [十二、迭代器模式](#十二迭代器模式)
+* [十三、组合模式](#十三组合模式)
+* [十四、状态模式](#十四状态模式)
+* [十五、代理模式](#十五代理模式)
+* [十六、MVC](#十六mvc)
+* [十七、与设计模式相处](#十七与设计模式相处)
-# 前言
+# 一、前言
-文中涉及一些 UML 类图,为了更好地理解,可以先阅读 [UML 类图](https://github.com/CyC2018/Interview-Notebook/blob/master/notes/%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1%E6%80%9D%E6%83%B3.md#1-%E7%B1%BB%E5%9B%BE)。
+文中涉及一些 UML 类图,为了更好地理解,可以先阅读 [UML 类图](https://github.com/CyC2018/Interview-Notebook/blob/master/notes/%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1%E6%80%9D%E6%83%B3.md#%E7%AC%AC%E4%B8%89%E7%AB%A0-uml)。
需要说明的一点是,文中的 UML 类图和规范的 UML 类图不大相同,其中组合关系使用以下箭头表示:
-# 设计模式入门
-
-**1. 设计模式概念**
+# 二、设计模式概念
设计模式不是代码,而是解决问题的方案,学习现有的设计模式可以做到经验复用。
拥有设计模式词汇,在沟通时就能用更少的词汇来讨论,并且不需要了解底层细节。
-**2. 问题描述**
+# 三、策略模式
+
+## 模式定义
+
+定义了算法族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。
+
+## 问题描述
设计不同种类的鸭子拥有不同的叫声和飞行方式。
-**3. 简单实现方案**
+## 简单实现方案
使用继承的解决方案如下,这种方案代码无法复用,如果两个鸭子类拥有同样的飞行方式,就有两份重复的代码。
-**4. 设计原则**
+## 设计原则
-封装变化 :在这里变化的是鸭子叫和飞行的行为方式。
+**封装变化** :在这里变化的是鸭子叫和飞行的行为方式。
-针对接口编程,而不是针对实现编程 :变量声明的类型为父类,而不是具体的某个子类。父类中的方法实现不在父类,而是在各个子类。程序在运行时可以动态改变变量所指向的子类类型。
+**针对接口编程,而不是针对实现编程** :变量声明的类型为父类,而不是具体的某个子类。父类中的方法实现不在父类,而是在各个子类。程序在运行时可以动态改变变量所指向的子类类型。
运用这一原则,将叫和飞行的行为抽象出来,实现多种不同的叫和飞行的子类,让子类去实现具体的叫和飞行方式。
-多用组合,少用继承:组合也就是 HAS-A 关系,通过组合,可以在运行时动态改变实现,只要通过改变父类对象具体指向哪个子类即可。而继承就不能做到这些,继承体系在创建类时就已经确定。
+**多用组合,少用继承** :组合也就是 HAS-A 关系,通过组合,可以在运行时动态改变实现,只要通过改变父类对象具体指向哪个子类即可。而继承就不能做到这些,继承体系在创建类时就已经确定。
运用这一原则,在 Duck 类中组合 FlyBehavior 和 QuackBehavior 类,performQuack() 和 performFly() 方法委托给这两个类去处理。通过这种方式,一个 Duck 子类可以根据需要去初始化 FlyBehavior 和 QuackBehavior 的子类对象,并且也可以动态地进行改变。
-**5. 整体设计图**
+## 问题的解决方案类图
-**6. 模式定义**
-
-策略模式:定义了算法族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。
-
-**7. 实现代码**
+## 代码实现
```java
public abstract class Duck {
@@ -172,15 +174,15 @@ FlyBehavior.FlyWithWings
FlyBehavior.FlyNoWay
```
-# 观察者模式
+# 三、观察者模式
-**1. 模式定义**
+## 模式定义
定义了对象之间的一对多依赖,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新。主题(Subject)是被观察的对象,而其所有依赖者(Observer)称为观察者。
-**2. 模式类图**
+## 模式类图
主题具有注册和移除观察者、并通知所有注册者的功能,主题是通过维护一张观察者列表来实现这些操作的。
@@ -188,19 +190,19 @@ FlyBehavior.FlyNoWay
-**3. 问题描述**
+## 问题描述
天气数据布告板会在天气信息发生改变时更新其内容,布告板有多个,并且在将来会继续增加。
-**4. 解决方案类图**
+## 问题的解决方案类图
-**5. 设计原则**
+## 设计原则
为交互对象之间的松耦合设计而努力:当两个对象之间松耦合,它们依然可以交互,但是不清楚彼此的细节。由于松耦合的两个对象之间互相依赖程度很低,因此系统具有弹性,能够应对变化。
-**6. 实现代码**
+## 代码实现
```java
public interface Subject {
@@ -304,13 +306,13 @@ CurrentConditionsDisplay.update:1.0 1.0 1.0
StatisticsDisplay.update:1.0 1.0 1.0
```
-# 装饰模式
+# 四、装饰模式
-**1. 问题描述**
+## 问题描述
设计不同种类的饮料,饮料可以添加配料,比如可以添加牛奶,并且支持动态添加新配料。每增加一种配料,该饮料的价格就会增加,要求计算一种饮料的价格。
-**2. 模式定义**
+## 模式定义
动态地将责任附加到对象上。在扩展功能上,装饰者提供了比继承更有弹性的替代方案。
@@ -318,25 +320,25 @@ StatisticsDisplay.update:1.0 1.0 1.0
-**3. 模式类图**
+## 模式类图
装饰者(Decorator)和具体组件(ConcreteComponent)都继承自组件(Component),具体组件的方法实现不需要依赖于其它对象,而装饰者组合了一个组件,这样它可以装饰其它装饰者或者具体组件。所谓装饰,就是把这个装饰者套在被装饰上,从而动态扩展被装饰者的功能。装饰者的方法有一部分是自己的,这属于它的功能,然后调用被装饰者的方法实现,从而也保留了被装饰者的功能。可以看到,具体组件应当是装饰层次的最低层,因为只有具体组件的方法实现不需要依赖于其它对象。
-**4. 问题的解决方案类图**
+## 问题的解决方案类图
-**5. 设计原则**
+## 设计原则
类应该对扩展开放,对修改关闭:也就是添加新功能时不需要修改代码。在本章问题中该原则体现在,饮料可以动态添加新的配料,而不需要去修改饮料的代码。观察则模式也符合这个原则。不可能把所有的类设计成都满足这一原则,应当把该原则应用于最有可能发生改变的地方。
-**6. Java I/O 中的装饰者模式**
+## Java I/O 中的装饰者模式
-**7. 代码实现**
+## 代码实现
```java
public interface Beverage {
@@ -407,15 +409,13 @@ public class StartbuzzCoffee {
3.0
```
-# 工厂模式
+# 五、简单工厂
-## 1. 简单工厂
-
-**1. 问题描述**
+## 问题描述
Pizza 类有很多子类,要求根据不同的情况用不同的子类实例化一个 Pizza 对象。
-**2. 定义**
+## 模式定义
简单工厂不是设计模式,更像是一种编程习惯。它把实例化的操作单独放到一个类中,这个类就成为简单工厂类,让简单工厂类来决定应该用哪个子类来实例化。
@@ -423,11 +423,11 @@ Pizza 类有很多子类,要求根据不同的情况用不同的子类实例
-**3. 问题的解决方案类图**
+## 问题的解决方案类图
-**4. 代码实现**
+## 代码实现
```java
public interface Pizza {
@@ -483,17 +483,17 @@ public class PizzaStore {
CheesePizza
```
-## 2. 工厂方法模式
+# 六、工厂方法模式
-**1. 问题描述**
+## 问题描述
每个地区的 PizzaStore 卖的 Pizza 虽然种类相同,但是都有自己的风味。一个客户点了纽约的 cheese 种类的 Pizza 和在芝加哥点的相同种类的 Pizza 是不同的。要求设计出满足条件的 PizzaStore。
-**2. 模式定义**
+## 2. 模式定义
定义了一个创建对象的接口,但由子类决定要实例化哪个类。工厂方法把实例化推迟到子类。
-**3. 模式类图**
+## 3. 模式类图
在简单工厂中,创建对象的是另一个类,而在工厂方法中,是由子类来创建对象。
@@ -501,19 +501,19 @@ CheesePizza
-**4. 问题的解决方案类图**
+## 4. 问题的解决方案类图
PizzaStore 有 orderPizza() 方法,顾客可以用它来下单。下单之后需要先使用 createPizza() 来制作 Pizza,这里的 createPizza() 就是 factoryMethod(),不同的 PizzaStore 子类实现了不同的 createPizza()。
-**5. 设计原则**
+## 5. 设计原则
依赖倒置原则:要依赖抽象,不要依赖具体类。听起来像是针对接口编程,不针对实现编程,但是这个原则说明了:不能让高层组件依赖底层组件,而且,不管高层或底层组件,两者都应该依赖于抽象。例如,下图中 Pizza 是抽象类,PizzaStore 和 Pizza 子类都依赖于 Pizza 这个抽象类。
-**6. 代码实现**
+## 6. 代码实现
```java
public interface Pizza {
@@ -609,13 +609,13 @@ NYStyleCheesePizza is making..
ChicagoStyleCheesePizza is making..
```
-## 3. 抽象工厂模式
+# 七、抽象工厂模式
-**1. 模式定义**
+## 模式定义
提供一个接口,用于创建 **相关的对象家族** 。
-**2. 模式类图**
+## 模式类图
抽象工厂模式创建的是对象家族,也就是很多对象而不是一个对象,并且这些对象是相关的,也就是说必须一起创建出来。而工厂模式只是用于创建一个对象,这和抽象工厂模式有很大不同。
@@ -627,11 +627,11 @@ ChicagoStyleCheesePizza is making..
-**3. 解决方案类图**
+## 解决方案类图
-**4. 代码实现**
+## 代码实现
```java
public interface Dough {
@@ -740,13 +740,13 @@ ThickCrustDough
MarinaraSauce
```
-# 单件模式
+# 八、单例模式
-**1. 模式定义**
+## 模式定义
确保一个类只有一个实例,并提供了一个全局访问点。
-**2. 模式类图**
+## 模式类图
使用一个私有构造器、一个私有静态变量以及一个公有静态函数来实现。
@@ -754,7 +754,7 @@ MarinaraSauce
-**3. 懒汉式-线程不安全**
+## 懒汉式-线程不安全
以下实现中,私有静态变量 uniqueInstance 被延迟化实例化,这样做的好处是,如果没有用到该类,那么就不会实例化 uniqueInstance,从而节约资源。
@@ -777,7 +777,7 @@ public class Singleton {
}
```
-**4. 懒汉式-线程安全**
+## 懒汉式-线程安全
只需要对 `getUniqueInstance()` 方法加锁,那么在一个时间点只能有一个线程能够进入该方法,从而避免了对 uniqueInstance 进行多次实例化的问题。
@@ -792,7 +792,7 @@ public static synchronized Singleton getUniqueInstance() {
}
```
-**5. 饿汉式-线程安全**
+## 饿汉式-线程安全
线程不安全问题主要是由于 uniqueInstance 被实例化了多次,如果 uniqueInstance 采用直接实例化的话,就不会被实例化多次,也就不会产生线程不安全问题。但是直接实例化的方式也丢失了延迟实例化带来的节约资源的优势。
@@ -800,7 +800,7 @@ public static synchronized Singleton getUniqueInstance() {
private static Singleton uniqueInstance = new Singleton();
```
-**6. 双重校验锁-线程安全**
+## 双重校验锁-线程安全
uniqueInstance 只需要被实例化一次,之后就可以直接使用了。加锁操作只需要对实例化那部分的代码进行。也就是说,只有当 uniqueInstance 没有被实例化时,才需要进行加锁。
@@ -837,9 +837,9 @@ if (uniqueInstance == null) {
}
```
-# 命令模式
+# 九、命令模式
-**1. 问题描述**
+## 问题描述
设计一个遥控器,它有很多按钮,每个按钮可以发起一个命令,命令会让一个家电完成相应操作。有非常多的家电,并且之后会增加家电。
@@ -849,11 +849,11 @@ if (uniqueInstance == null) {
-**2. 模式定义**
+## 模式定义
将命令封装成对象,以便使用不同的命令来参数化其它对象。
-**3. 问题的解决方案类图**
+## 问题的解决方案类图
- RemoteControl 是遥控器,它可以为每个按钮设置命令对象,并且执行命令。
@@ -865,11 +865,11 @@ if (uniqueInstance == null) {
-**4. 模式类图**
+## 模式类图
-**5. 代码实现**
+## 代码实现
```java
public interface Command {
@@ -948,31 +948,31 @@ public class RemoteLoader {
Light is on!
```
-# 适配器模式
+# 十、适配器模式
-**1. 模式定义**
+## 模式定义
将一个类的接口,转换为客户期望的另一个接口。适配器让原本不兼容的类可以合作无间。
-**2. 模式类图**
+## 模式类图
适配器(Adapter)组合一个适配者(Adaptee),Adapter 把操作委托给 Adaptee。
-**3. 问题描述**
+## 问题描述
鸭子(Duck)和火鸡(Turkey)拥有不同的叫声,Duck 的叫声调用 quack() 方法,而 Turkey 调用 gobble() 方法。
要求将 Turkey 的 gobble() 方法适配成 Duck 的 quack() 方法,从而让火鸡冒充鸭子!
-**4. 解决方案类图**
+## 问题的解决方案类图
-**5. 代码实现**
+## 代码实现
```java
public interface Duck {
@@ -1026,69 +1026,69 @@ public class DuckTestDrive {
gobble!
```
-# 外观模式
+# 十、外观模式
-**1. 模式定义**
+## 模式定义
提供了一个统一的接口,用来访问子系统中的一群接口,从而让子系统更容易使用。
-**2. 模式类图**
+## 模式类图
-**3. 问题描述**
+## 问题描述
家庭影院中有众多电器,当要进行观看电影时需要对很多电器进行操作。要求简化这些操作,使得家庭影院类只提供一个简化的接口,例如提供一个看电影相关的接口。
-**4. 解决方案类图**
+## 解决方案类图
-**5. 设计原则**
+## 设计原则
-最少知识原则:只和你的密友谈话。也就是客户对象所需要交互的对象应当尽可能少。
+**最少知识原则** :只和你的密友谈话。也就是客户对象所需要交互的对象应当尽可能少。
-**6. 代码实现**
+## 代码实现
过于简单,无实现。
-# 模板方法模式
+# 十一、模板方法模式
-**1. 模式定义**
+## 模式定义
在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。
模板方法使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤。
-**2. 模式类图**
+## 模式类图
模板方法 templateMethod() 定义了算法的骨架,确定了 primitiveOperation1() 和 primitiveOperation2() 方法执行的顺序,而 primitiveOperation1() 和 primitiveOperation2() 让子类去实现。
-**3. 问题描述**
+## 问题描述
冲咖啡和冲茶都有类似的流程,但是某些步骤会有点不一样,要求复用那些相同步骤的代码。
-**4. 问题的解决方案类图**
+## 问题的解决方案类图
prepareRecipe() 方法就是模板方法,它确定了其它四个方法的具体执行步骤。其中 brew() 和 addCondiments() 方法在子类中实现。
-**5. 设计原则**
+## 设计原则
-好莱坞原则:别调用(打电话给)我们,我们会调用(打电话给)你。这一原则可以防止依赖腐败,即防止高层组件依赖低层组件,低层组件又依赖高层组件。该原则在模板方法的体现为,只有父类会调用子类,子类不会调用父类。
+**好莱坞原则** :别调用(打电话给)我们,我们会调用(打电话给)你。这一原则可以防止依赖腐败,即防止高层组件依赖低层组件,低层组件又依赖高层组件。该原则在模板方法的体现为,只有父类会调用子类,子类不会调用父类。
-**6. 钩子**
+## 钩子
某些步骤在不同实现中可有可无,可以先定义一个什么都不做的方法,把它加到模板方法中,如果子类需要它就覆盖默认实现并加上自己的实现。
-**7. 代码实现**
+## 代码实现
```java
public abstract class CaffeineBeverage {
@@ -1168,13 +1168,13 @@ pourInCup
Tea.addCondiments
```
-# 迭代器模式
+# 十二、迭代器模式
-**1. 模式定义**
+## 模式定义
提供顺序访问一个聚合对象中的各个元素的方法,而又不暴露聚合对象内部的表示。
-**2. 模式类图**
+## 模式类图
- Aggregate 是聚合类,其中 createIterator() 方法可以产生一个 Iterator;
@@ -1184,7 +1184,7 @@ Tea.addCondiments
-**3. 代码实现**
+## 代码实现
```java
public class Aggregate {
@@ -1258,7 +1258,7 @@ public class Client {
9
```
-**4. Java 内置的迭代器**
+## Java 内置的迭代器
需要让聚合类实现 Iterable 接口,该接口有一个 iterator() 方法会返回一个 Iterator 对象。
@@ -1320,19 +1320,19 @@ public class Client {
}
```
-# 组合模式
+# 十三、组合模式
-**1. 设计原则**
+## 设计原则
一个类应该只有一个引起它改变的原因。
-**2. 模式定义**
+## 模式定义
允许将对象组合成树形结构来表现“整体/部分”关系。
组合能让客户以一致的方式处理个别对象以及组合对象。
-**3. 模式类图**
+## 模式类图
组件(Component)类是组合类(Composite)和叶子类(Leaf)的父类,可以把组合类看成是树的中间节点。
@@ -1340,7 +1340,7 @@ public class Client {
-**4. 代码实现**
+## 代码实现
```java
public abstract class Component {
@@ -1441,19 +1441,19 @@ Composite:root
--left:3
```
-# 状态模式
+# 十四、状态模式
-**1. 模式定义**
+## 模式定义
允许对象在内部状态改变时改变它的行为,对象看起来好像修改了它所属的类。
-**2. 模式类图**
+## 模式类图
Context 的 request() 方法委托给 State 对象去处理。当 Context 组合的 State 对象发生改变时,它的行为也就发生了改变。
-**3. 与策略模式的比较**
+## 与策略模式的比较
状态模式的类图和策略模式一样,并且都是能够动态改变对象的行为。
@@ -1463,13 +1463,13 @@ Context 的 request() 方法委托给 State 对象去处理。当 Context 组合
状态模式主要是用来解决状态转移的问题,当状态发生转移了,那么 Context 对象就会改变它的行为;而策略模式主要是用来封装一组可以互相替代的算法族,并且可以根据需要动态地去替换 Context 需要使用哪个算法。
-**4. 问题描述**
+## 问题描述
糖果销售机有多种状态,每种状态下销售机有不同的行为,状态可以发生转移,使得销售机的行为也发生改变。
-**5. 直接解决方案**
+## 直接解决方案
在糖果机的每个操作函数里面,判断当前的状态,根据不同的状态进行不同的处理,并且发生不同的状态转移。
@@ -1477,7 +1477,7 @@ Context 的 request() 方法委托给 State 对象去处理。当 Context 组合
-**6 代码实现**
+## 代码实现
糖果销售机即 Context。
@@ -1763,34 +1763,36 @@ You turned, but there are no gumballs
No gumball dispensed
```
-# 代理模式
+# 十五、代理模式
-# 复合模式
+# 十六、MVC
-## MVC
-
-**传统 MVC**
+## 传统 MVC
视图使用组合模式,模型使用了观察者模式,控制器使用了策略模式。
-**Web 中的 MVC**
+## Web 中的 MVC
模式不再使用观察者模式。
-# 与设计模式相处
+# 十七、与设计模式相处
-定义:在某情境下,针对某问题的某种解决方案。
+## 定义
+
+在某情境下,针对某问题的某种解决方案。
+
+## 何时使用
过度使用设计模式可能导致代码被过度工程化,应该总是用最简单的解决方案完成工作,并在真正需要模式的地方才使用它。
-反模式:不好的解决方案来解决一个问题。主要作用是为了警告人们不要使用这些解决方案。
+## 反模式
-模式分类:
+不好的解决方案来解决一个问题。主要作用是为了警告人们不要使用这些解决方案。
+
+## 模式分类
-
-# 剩下的模式