auto commit
This commit is contained in:
parent
7cf8605f75
commit
fa890fd48b
@ -214,7 +214,7 @@ public interface Externalizable extends Serializable {
|
||||
|
||||
<font size=4> **3. 比较** </font> </br>
|
||||
|
||||
| | ** 抽象类** | ** 接口** |
|
||||
| | **抽象类** | **接口** |
|
||||
| --- | --- | --- |
|
||||
| 1 | An abstract class can extend only one class or one abstract class at a time | An interface can extend any number of interfaces at a time |
|
||||
| 2 | An abstract class can extend another concrete (regular) class or abstract class | An interface can only extend another interface |
|
||||
|
112
notes/Java 并发.md
112
notes/Java 并发.md
@ -1,29 +1,29 @@
|
||||
<!-- GFM-TOC -->
|
||||
* [使用线程](#使用线程)
|
||||
* [1. 实现 Runnable 接口](#1-实现-runnable-接口)
|
||||
* [2. 实现 Callable 接口](#2-实现-callable-接口)
|
||||
* [3. 继承 Thread 类](#3-继承-thread-类)
|
||||
* [4. 实现接口 vs 继承 Thread](#4-实现接口-vs-继承-thread)
|
||||
* [Executor](#executor)
|
||||
* [基础线程机制](#基础线程机制)
|
||||
* [1. sleep()](#1-sleep)
|
||||
* [2. yield()](#2-yield)
|
||||
* [3. join()](#3-join)
|
||||
* [4. deamon](#4-deamon)
|
||||
* [线程之间的协作](#线程之间的协作)
|
||||
* [1. 线程通信](#1-线程通信)
|
||||
* [2. 线程同步](#2-线程同步)
|
||||
* [2.1 synchronized](#21-synchronized)
|
||||
* [2.2 Lock](#22-lock)
|
||||
* [2.3 BlockingQueue](#23-blockingqueue)
|
||||
* [结束线程](#结束线程)
|
||||
* [1. 阻塞](#1-阻塞)
|
||||
* [2. 中断](#2-中断)
|
||||
* [线程状态转换](#线程状态转换)
|
||||
* [volatile](#volatile)
|
||||
* [1. 内存可见性](#1-内存可见性)
|
||||
* [2. 禁止指令重排](#2-禁止指令重排)
|
||||
* [内存模型](#内存模型)
|
||||
* [一、使用线程](#一使用线程)
|
||||
* [实现 Runnable 接口](#实现-runnable-接口)
|
||||
* [实现 Callable 接口](#实现-callable-接口)
|
||||
* [继承 Thread 类](#继承-thread-类)
|
||||
* [实现接口 VS 继承 Thread](#实现接口-vs-继承-thread)
|
||||
* [二、Executor](#二executor)
|
||||
* [三、基础线程机制](#三基础线程机制)
|
||||
* [sleep()](#sleep)
|
||||
* [yield()](#yield)
|
||||
* [join()](#join)
|
||||
* [deamon](#deamon)
|
||||
* [四、线程之间的协作](#四线程之间的协作)
|
||||
* [线程通信](#线程通信)
|
||||
* [线程同步](#线程同步)
|
||||
* [1. synchronized](#1-synchronized)
|
||||
* [2. Lock](#2-lock)
|
||||
* [3. BlockingQueue](#3-blockingqueue)
|
||||
* [五、结束线程](#五结束线程)
|
||||
* [阻塞](#阻塞)
|
||||
* [中断](#中断)
|
||||
* [六、线程状态转换](#六线程状态转换)
|
||||
* [七、volatile](#七volatile)
|
||||
* [保证内存可见性](#保证内存可见性)
|
||||
* [禁止指令重排](#禁止指令重排)
|
||||
* [八、内存模型](#八内存模型)
|
||||
* [1. 硬件的效率与一致性](#1-硬件的效率与一致性)
|
||||
* [2. Java 内存模型](#2-java-内存模型)
|
||||
* [3. 主内存与工作内存](#3-主内存与工作内存)
|
||||
@ -33,7 +33,7 @@
|
||||
* [5.2 可见性](#52-可见性)
|
||||
* [5.3 有序性](#53-有序性)
|
||||
* [6. 先行发生原则](#6-先行发生原则)
|
||||
* [线程安全](#线程安全)
|
||||
* [九、线程安全](#九线程安全)
|
||||
* [1. Java 语言中的线程安全](#1-java-语言中的线程安全)
|
||||
* [1.1 不可变](#11-不可变)
|
||||
* [1.2 绝对线程安全](#12-绝对线程安全)
|
||||
@ -44,18 +44,18 @@
|
||||
* [2.1 互斥同步](#21-互斥同步)
|
||||
* [2.2 非阻塞同步](#22-非阻塞同步)
|
||||
* [2.3 无同步方案](#23-无同步方案)
|
||||
* [锁优化](#锁优化)
|
||||
* [十、锁优化](#十锁优化)
|
||||
* [1. 自旋锁与自适应自旋](#1-自旋锁与自适应自旋)
|
||||
* [2. 锁消除](#2-锁消除)
|
||||
* [3. 锁粗化](#3-锁粗化)
|
||||
* [4. 轻量级锁](#4-轻量级锁)
|
||||
* [5. 偏向锁](#5-偏向锁)
|
||||
* [多线程开发良好的实践](#多线程开发良好的实践)
|
||||
* [十一、多线程开发良好的实践](#十一多线程开发良好的实践)
|
||||
* [参考资料](#参考资料)
|
||||
<!-- GFM-TOC -->
|
||||
|
||||
|
||||
# 使用线程
|
||||
# 一、使用线程
|
||||
|
||||
有三种使用线程的方法:
|
||||
|
||||
@ -65,7 +65,7 @@
|
||||
|
||||
实现 Runnable 和 Callable 接口的类只能当做一个可以在线程中运行的任务,不是真正意义上的线程,因此最后还需要通过 Thread 来调用。可以说任务是通过线程驱动从而执行的。
|
||||
|
||||
## 1. 实现 Runnable 接口
|
||||
## 实现 Runnable 接口
|
||||
|
||||
需要实现 run() 方法。
|
||||
|
||||
@ -84,7 +84,7 @@ public class MyRunnable implements Runnable {
|
||||
}
|
||||
```
|
||||
|
||||
## 2. 实现 Callable 接口
|
||||
## 实现 Callable 接口
|
||||
|
||||
与 Runnable 相比,Callable 可以有返回值,返回值通过 FutureTask 进行封装。
|
||||
|
||||
@ -103,7 +103,7 @@ public class MyCallable implements Callable<Integer> {
|
||||
}
|
||||
```
|
||||
|
||||
## 3. 继承 Thread 类
|
||||
## 继承 Thread 类
|
||||
|
||||
同样也是需要实现 run() 方法,并且最后也是调用 start() 方法来启动线程。
|
||||
|
||||
@ -119,14 +119,14 @@ public class MyThread extends Thread {
|
||||
}
|
||||
```
|
||||
|
||||
## 4. 实现接口 vs 继承 Thread
|
||||
## 实现接口 VS 继承 Thread
|
||||
|
||||
实现接口会更好一些,因为:
|
||||
|
||||
1. Java 不支持多重继承,因此继承了 Thread 类就无法继承其它类,但是可以实现多个接口;
|
||||
2. 类可能只要求可执行即可,继承整个 Thread 类开销会过大。
|
||||
|
||||
# Executor
|
||||
# 二、Executor
|
||||
|
||||
Executor 管理多个异步任务的执行,而无需程序员显示地管理线程的生命周期。
|
||||
|
||||
@ -144,9 +144,9 @@ for(int i = 0; i < 5; i++) {
|
||||
}
|
||||
```
|
||||
|
||||
# 基础线程机制
|
||||
# 三、基础线程机制
|
||||
|
||||
## 1. sleep()
|
||||
## sleep()
|
||||
|
||||
**Thread.sleep(millisec)** 方法会休眠当前正在执行的线程,millisec 单位为毫秒。也可以使用 TimeUnit.TILLISECONDS.sleep(millisec)。
|
||||
|
||||
@ -164,7 +164,7 @@ public void run() {
|
||||
}
|
||||
```
|
||||
|
||||
## 2. yield()
|
||||
## yield()
|
||||
|
||||
对静态方法 **Thread.yield()** 的调用声明了当前线程已经完成了生命周期中最重要的部分,可以切换给其它线程来执行。
|
||||
|
||||
@ -175,13 +175,13 @@ public void run() {
|
||||
}
|
||||
```
|
||||
|
||||
## 3. join()
|
||||
## join()
|
||||
|
||||
在线程中调用另一个线程的 **join()** 方法,会将当前线程挂起,直到目标线程结束。
|
||||
|
||||
可以加一个超时参数。
|
||||
|
||||
## 4. deamon
|
||||
## deamon
|
||||
|
||||
后台线程( **deamon** )是程序运行时在后台提供服务的线程,并不属于程序中不可或缺的部分。
|
||||
|
||||
@ -191,14 +191,14 @@ main() 属于非后台线程。
|
||||
|
||||
使用 setDaemon() 方法将一个线程设置为后台线程。
|
||||
|
||||
# 线程之间的协作
|
||||
# 四、线程之间的协作
|
||||
|
||||
- **线程通信** :保证线程以一定的顺序执行;
|
||||
- **线程同步** :保证线程对临界资源的互斥访问。
|
||||
|
||||
线程通信往往是基于线程同步的基础上完成的,因此很多线程通信问题也是线程同步问题。
|
||||
|
||||
## 1. 线程通信
|
||||
## 线程通信
|
||||
|
||||
**wait()、notify() 和 notifyAll()** 三者实现了线程之间的通信。
|
||||
|
||||
@ -229,11 +229,11 @@ public synchronized void before() {
|
||||
1. wait() 是 Object 类的方法,而 sleep() 是 Thread 的静态方法;
|
||||
2. wait() 会放弃锁,而 sleep() 不会。
|
||||
|
||||
## 2. 线程同步
|
||||
## 线程同步
|
||||
|
||||
给定一个进程内的所有线程,都共享同一存储空间,这样有好处又有坏处。这些线程就可以共享数据,非常有用。不过,在两个线程同时修改某一资源时,这也会造成一些问题。Java 提供了同步机制,以控制对共享资源的互斥访问。
|
||||
|
||||
### 2.1 synchronized
|
||||
### 1. synchronized
|
||||
|
||||
**同步一个方法**
|
||||
|
||||
@ -255,7 +255,7 @@ public void func(String name) {
|
||||
}
|
||||
```
|
||||
|
||||
### 2.2 Lock
|
||||
### 2. Lock
|
||||
|
||||
若要实现更细粒度的控制,我们可以使用锁(lock)。
|
||||
|
||||
@ -271,7 +271,7 @@ public int func(int value) {
|
||||
}
|
||||
```
|
||||
|
||||
### 2.3 BlockingQueue
|
||||
### 3. BlockingQueue
|
||||
|
||||
java.util.concurrent.BlockingQueue 接口有以下阻塞队列的实现:
|
||||
|
||||
@ -365,9 +365,9 @@ Consumer3 is consuming product made by Consumer3...
|
||||
Consumer4 is consuming product made by Consumer4...
|
||||
```
|
||||
|
||||
# 结束线程
|
||||
# 五、结束线程
|
||||
|
||||
## 1. 阻塞
|
||||
## 阻塞
|
||||
|
||||
一个线程进入阻塞状态可能有以下原因:
|
||||
|
||||
@ -376,7 +376,7 @@ Consumer4 is consuming product made by Consumer4...
|
||||
3. 等待某个 I/O 的完成;
|
||||
4. 试图在某个对象上调用其同步控制方法,但是对象锁不可用,因为另一个线程已经获得了这个锁。
|
||||
|
||||
## 2. 中断
|
||||
## 中断
|
||||
|
||||
使用中断机制即可终止阻塞的线程。
|
||||
|
||||
@ -402,7 +402,7 @@ interrupt() 方法会设置中断状态,可以通过 interrupted() 方法来
|
||||
|
||||
interrupted() 方法在检查完中断状态之后会清除中断状态,这样做是为了确保一次中断操作只会产生一次影响。
|
||||
|
||||
# 线程状态转换
|
||||
# 六、线程状态转换
|
||||
|
||||
<div align="center"> <img src="../pics//38b894a7-525e-4204-80de-ecc1acc52c46.jpg"/> </div><br>
|
||||
|
||||
@ -427,11 +427,11 @@ interrupted() 方法在检查完中断状态之后会清除中断状态,这样
|
||||
- LockSupport.parkNanos() 方法
|
||||
- LockSupport.parkUntil() 方法
|
||||
|
||||
# volatile
|
||||
# 七、volatile
|
||||
|
||||
保证了内存可见性和禁止指令重排,没法保证原子性。
|
||||
|
||||
## 1. 内存可见性
|
||||
## 保证内存可见性
|
||||
|
||||
普通共享变量被修改之后,什么时候被写入主存是不确定的。
|
||||
|
||||
@ -439,7 +439,7 @@ volatile 关键字会保证每次修改共享变量之后该值会立即更新
|
||||
|
||||
synchronized 和 Lock 也能够保证内存可见性。它们能保证同一时刻只有一个线程获取锁然后执行同步代码,并且在释放锁之前会将对变量的修改刷新到主存当中。不过只有对共享变量的 set() 和 get() 方法都加上 synchronized 才能保证可见性,如果只有 set() 方法加了 synchronized,那么 get() 方法并不能保证会从内存中读取最新的数据。
|
||||
|
||||
## 2. 禁止指令重排
|
||||
## 禁止指令重排
|
||||
|
||||
在 Java 内存模型中,允许编译器和处理器对指令进行重排序,重排序过程不会影响到单线程程序的执行,却会影响到多线程并发执行的正确性。
|
||||
|
||||
@ -447,7 +447,7 @@ volatile 关键字通过添加内存屏障的方式来进制指令重排,即
|
||||
|
||||
可以通过 synchronized 和 Lock 来保证有序性,它们保证每个时刻只有一个线程执行同步代码,相当于是让线程顺序执行同步代码,自然就保证了有序性。
|
||||
|
||||
# 内存模型
|
||||
# 八、内存模型
|
||||
|
||||
## 1. 硬件的效率与一致性
|
||||
|
||||
@ -581,7 +581,7 @@ int j = 2;
|
||||
|
||||
上面两个例子综合起来证明了一个结论:时间先后顺序与先行发生原则之间基本没有太大的关系,所以我们衡量并发安全问题的时候不要受到时间顺序的干扰,一切必须以先行发生原则为准。
|
||||
|
||||
# 线程安全
|
||||
# 九、线程安全
|
||||
|
||||
《Java Concurrency In Practice》的作者 Brian Goetz 对“线程安全”有一个比较恰当的定义:“当多个线程访问一个对象时,如果不用考虑这些线程在运行时环境下的调度和交替执行,也不需要进行额外的同步,或者在调用方进行任何其他的协调操作,调用这个对象的行为都可以获得正确的结果,那这个对象是线程安全的”。
|
||||
|
||||
@ -830,7 +830,7 @@ incrementAndGet() 方法在一个无限循环中,不断尝试将一个比当
|
||||
|
||||
Java 语言中,如果一个变量要被多线程访问,可以使用 volatile 关键字声明它为“易变的”;如果一个变量要被某个线程独享,Java 中就没有类似 C++中 \_\_declspec(thread)这样的关键字,不过还是可以通过 java.lang.ThreadLocal 类来实现线程本地存储的功能。每一个线程的 Thread 对象中都有一个 ThreadLocalMap 对象,这个对象存储了一组以 ThreadLocal.threadLocalHashCode 为键,以本地线程变量为值的 K-V 值对,ThreadLocal 对象就是当前线程的 ThreadLocalMap 的访问入口,每一个 ThreadLocal 对象都包含了一个独一无二的 threadLocalHashCode 值,使用这个值就可以在线程 K-V 值对中找回对应的本地线程变量。
|
||||
|
||||
# 锁优化
|
||||
# 十、锁优化
|
||||
|
||||
高效并发是从 JDK 1.5 到 JDK 1.6 的一个重要改进,HotSpot 虚拟机开发团队在这个版本上花费了大量的精力去实现各种锁优化技术,如适应性自旋(Adaptive Spinning)、锁消除(Lock Elimination)、锁粗化(Lock Coarsening)、轻量级锁(Lightweight Locking)和偏向锁(Biased Locking)等。这些技术都是为了在线程之间更高效地共享数据,以及解决竞争问题,从而提高程序的执行效率。
|
||||
|
||||
@ -917,7 +917,7 @@ public static String concatString(String s1, String s2, String s3) {
|
||||
|
||||
偏向锁可以提高带有同步但无竞争的程序性能。它同样是一个带有效益权衡(Trade Off)性质的优化,也就是说,它并不一定总是对程序运行有利,如果程序中大多数的锁总是被多个不同的线程访问,那偏向模式就是多余的。在具体问题具体分析的前提下,有时候使用参数 -XX:-UseBiasedLocking 来禁止偏向锁优化反而可以提升性能。
|
||||
|
||||
# 多线程开发良好的实践
|
||||
# 十一、多线程开发良好的实践
|
||||
|
||||
- 给线程命名。
|
||||
- 最小化同步范围。
|
||||
|
@ -30,9 +30,7 @@
|
||||
|
||||
<div align="center"> <img src="../pics//4f4deaf4-8487-4de2-9d62-5ad017ee9589.png"/> </div><br>
|
||||
|
||||
事务指的是满足 ACID 特性的一系列操作。
|
||||
|
||||
在数据库中,可以通过 Commit 提交一个事务,也可以使用 Rollback 进行回退。
|
||||
事务指的是满足 ACID 特性的一系列操作。在数据库中,可以通过 Commit 提交一个事务,也可以使用 Rollback 进行回退。
|
||||
|
||||
## 四大特性
|
||||
|
||||
|
172
notes/重构.md
172
notes/重构.md
@ -1,6 +1,6 @@
|
||||
<!-- GFM-TOC -->
|
||||
* [第一个案例](#第一个案例)
|
||||
* [重构原则](#重构原则)
|
||||
* [一、第一个案例](#一第一个案例)
|
||||
* [二、重构原则](#二重构原则)
|
||||
* [定义](#定义)
|
||||
* [为何重构](#为何重构)
|
||||
* [三次法则](#三次法则)
|
||||
@ -9,31 +9,31 @@
|
||||
* [何时不该重构](#何时不该重构)
|
||||
* [重构与设计](#重构与设计)
|
||||
* [重构与性能](#重构与性能)
|
||||
* [代码的坏味道](#代码的坏味道)
|
||||
* [1. Duplicated Code(重复代码)](#1-duplicated-code重复代码)
|
||||
* [2. Long Method(过长函数)](#2-long-method过长函数)
|
||||
* [3. Large Class(过大的类)](#3-large-class过大的类)
|
||||
* [4. Long Parameter List(过长的参数列表)](#4-long-parameter-list过长的参数列表)
|
||||
* [5. Divergent Change(发散式变化)](#5-divergent-change发散式变化)
|
||||
* [6. Shotgun Surgery(散弹式修改)](#6-shotgun-surgery散弹式修改)
|
||||
* [7. Feature Envy(依恋情结)](#7-feature-envy依恋情结)
|
||||
* [8. Data Clumps(数据泥团)](#8-data-clumps数据泥团)
|
||||
* [9. Primitive Obsession(基本类型偏执)](#9-primitive-obsession基本类型偏执)
|
||||
* [10. Switch Statements(switch 惊悚现身)](#10-switch-statementsswitch-惊悚现身)
|
||||
* [11. Parallel Inheritance Hierarchies(平行继承体系)](#11-parallel-inheritance-hierarchies平行继承体系)
|
||||
* [12. Lazy Class(冗余类)](#12-lazy-class冗余类)
|
||||
* [13. Speculative Generality(夸夸其谈未来性)](#13-speculative-generality夸夸其谈未来性)
|
||||
* [14. Temporary Field(令人迷惑的暂时字段)](#14-temporary-field令人迷惑的暂时字段)
|
||||
* [15. Message Chains(过度耦合的消息链)](#15-message-chains过度耦合的消息链)
|
||||
* [16. Middle Man(中间人)](#16-middle-man中间人)
|
||||
* [17. Inappropriate Intimacy(狎昵关系)](#17-inappropriate-intimacy狎昵关系)
|
||||
* [18. Alernative Classes with Different Interfaces(异曲同工的类)](#18-alernative-classes-with-different-interfaces异曲同工的类)
|
||||
* [19. Incomplete Library Class(不完美的类库)](#19-incomplete-library-class不完美的类库)
|
||||
* [20. Data Class(幼稚的数据类)](#20-data-class幼稚的数据类)
|
||||
* [21. Refused Bequest(被拒绝的馈赠)](#21-refused-bequest被拒绝的馈赠)
|
||||
* [22. Comments(过多的注释)](#22-comments过多的注释)
|
||||
* [构筑测试体系](#构筑测试体系)
|
||||
* [重新组织函数](#重新组织函数)
|
||||
* [三、代码的坏味道](#三代码的坏味道)
|
||||
* [1. 重复代码](#1-重复代码)
|
||||
* [2. 过长函数](#2-过长函数)
|
||||
* [3. 过大的类](#3-过大的类)
|
||||
* [4. 过长的参数列表](#4-过长的参数列表)
|
||||
* [5. 发散式变化](#5-发散式变化)
|
||||
* [6. 散弹式修改](#6-散弹式修改)
|
||||
* [7. 依恋情结](#7-依恋情结)
|
||||
* [8. 数据泥团](#8-数据泥团)
|
||||
* [9. 基本类型偏执](#9-基本类型偏执)
|
||||
* [10. switch 惊悚现身](#10-switch-惊悚现身)
|
||||
* [11. 平行继承体系](#11-平行继承体系)
|
||||
* [12. 冗余类](#12-冗余类)
|
||||
* [13. 夸夸其谈未来性](#13-夸夸其谈未来性)
|
||||
* [14. 令人迷惑的暂时字段](#14-令人迷惑的暂时字段)
|
||||
* [15. 过度耦合的消息链](#15-过度耦合的消息链)
|
||||
* [16. 中间人](#16-中间人)
|
||||
* [17. 狎昵关系](#17-狎昵关系)
|
||||
* [18. 异曲同工的类](#18-异曲同工的类)
|
||||
* [19. 不完美的类库](#19-不完美的类库)
|
||||
* [20. 幼稚的数据类](#20-幼稚的数据类)
|
||||
* [21. 被拒绝的馈赠](#21-被拒绝的馈赠)
|
||||
* [22. 过多的注释](#22-过多的注释)
|
||||
* [四、构筑测试体系](#四构筑测试体系)
|
||||
* [五、重新组织函数](#五重新组织函数)
|
||||
* [1. Extract Method(提炼函数)](#1-extract-method提炼函数)
|
||||
* [2. Inline Method(内联函数)](#2-inline-method内联函数)
|
||||
* [3. Inline Temp(内联临时变量)](#3-inline-temp内联临时变量)
|
||||
@ -43,7 +43,7 @@
|
||||
* [7. Remove Assigments to Parameters(移除对参数的赋值)](#7-remove-assigments-to-parameters移除对参数的赋值)
|
||||
* [8. Replace Method with Method Object(以函数对象取代函数)](#8-replace-method-with-method-object以函数对象取代函数)
|
||||
* [9. Subsititute Algorithn(替换算法)](#9-subsititute-algorithn替换算法)
|
||||
* [在对象之间搬移特性](#在对象之间搬移特性)
|
||||
* [六、在对象之间搬移特性](#六在对象之间搬移特性)
|
||||
* [1. Move Method(搬移函数)](#1-move-method搬移函数)
|
||||
* [2. Move Field(搬移字段)](#2-move-field搬移字段)
|
||||
* [3. Extract Class(提炼类)](#3-extract-class提炼类)
|
||||
@ -52,7 +52,7 @@
|
||||
* [6. Remove Middle Man(移除中间人)](#6-remove-middle-man移除中间人)
|
||||
* [7. Introduce Foreign Method(引入外加函数)](#7-introduce-foreign-method引入外加函数)
|
||||
* [8. Introduce Local Extension(引入本地扩展)](#8-introduce-local-extension引入本地扩展)
|
||||
* [重新组织数据](#重新组织数据)
|
||||
* [七、重新组织数据](#七重新组织数据)
|
||||
* [1. Self Encapsulate Field(自封装字段)](#1-self-encapsulate-field自封装字段)
|
||||
* [2. Replace Data Value with Object(以对象取代数据值)](#2-replace-data-value-with-object以对象取代数据值)
|
||||
* [3. Change Value to Reference(将值对象改成引用对象)](#3-change-value-to-reference将值对象改成引用对象)
|
||||
@ -69,7 +69,7 @@
|
||||
* [14. Replace Type Code with Subcalsses(以子类取代类型码)](#14-replace-type-code-with-subcalsses以子类取代类型码)
|
||||
* [15. Replace Type Code with State/Strategy (以 State/Strategy 取代类型码)](#15-replace-type-code-with-statestrategy-以-statestrategy-取代类型码)
|
||||
* [16. Replace Subclass with Fields(以字段取代子类)](#16-replace-subclass-with-fields以字段取代子类)
|
||||
* [简化条件表达式](#简化条件表达式)
|
||||
* [八、简化条件表达式](#八简化条件表达式)
|
||||
* [1. Decompose Conditional(分解条件表达式)](#1-decompose-conditional分解条件表达式)
|
||||
* [2. Consolidate Conditional Expression(合并条件表达式)](#2-consolidate-conditional-expression合并条件表达式)
|
||||
* [3. Consolidate Duplicate Conditional Fragments (合并重复的条件片段)](#3-consolidate-duplicate-conditional-fragments-合并重复的条件片段)
|
||||
@ -78,7 +78,7 @@
|
||||
* [6. Replace Conditional with Polymorphism (以多态取代条件表达式)](#6-replace-conditional-with-polymorphism-以多态取代条件表达式)
|
||||
* [7. Introduce Null Object(引入Null对象)](#7-introduce-null-object引入null对象)
|
||||
* [8. Introduce Assertion(引入断言)](#8-introduce-assertion引入断言)
|
||||
* [简化函数调用](#简化函数调用)
|
||||
* [九、简化函数调用](#九简化函数调用)
|
||||
* [1. Rename Method(函数改名)](#1-rename-method函数改名)
|
||||
* [2. Add Parameter(添加参数)](#2-add-parameter添加参数)
|
||||
* [3. Remove Parameter(移除参数)](#3-remove-parameter移除参数)
|
||||
@ -94,7 +94,7 @@
|
||||
* [13. Encapsulate Downcast(封装向下转型)](#13-encapsulate-downcast封装向下转型)
|
||||
* [14. Replace Error Code with Exception (以异常取代错误码)](#14-replace-error-code-with-exception-以异常取代错误码)
|
||||
* [15. Replace Exception with Test(以测试取代异常)](#15-replace-exception-with-test以测试取代异常)
|
||||
* [处理概括关系](#处理概括关系)
|
||||
* [十、处理概括关系](#十处理概括关系)
|
||||
* [1. Pull Up Field(字段上移)](#1-pull-up-field字段上移)
|
||||
* [2. Pull Up Method(函数上移)](#2-pull-up-method函数上移)
|
||||
* [3. Pull Up Constructor Body(构造函数本体上移)](#3-pull-up-constructor-body构造函数本体上移)
|
||||
@ -110,7 +110,7 @@
|
||||
<!-- GFM-TOC -->
|
||||
|
||||
|
||||
# 第一个案例
|
||||
# 一、第一个案例
|
||||
|
||||
如果你发现自己需要为程序添加一个特性,而代码结构使你无法很方便地达成目的,那就先重构这个程序。
|
||||
|
||||
@ -173,7 +173,7 @@ double getTotalCharge() {
|
||||
|
||||
<div align="center"> <img src="../pics//2c8a7a87-1bf1-4d66-9ba9-225a1add0a51.png"/> </div><br>
|
||||
|
||||
# 重构原则
|
||||
# 二、重构原则
|
||||
|
||||
## 定义
|
||||
|
||||
@ -229,13 +229,15 @@ double getTotalCharge() {
|
||||
|
||||
应当只关注关键代码的性能,因为只有一小部分的代码是关键代码。
|
||||
|
||||
# 代码的坏味道
|
||||
# 三、代码的坏味道
|
||||
|
||||
本章主要介绍一些不好的代码,也就是说这些代码应该被重构。
|
||||
|
||||
文中提到的具体重构原则可以先忽略。
|
||||
|
||||
## 1. Duplicated Code(重复代码)
|
||||
## 1. 重复代码
|
||||
|
||||
> Duplicated Code
|
||||
|
||||
同一个类的两个函数有相同表达式,则用 Extract Method 提取出重复代码;
|
||||
|
||||
@ -245,7 +247,9 @@ double getTotalCharge() {
|
||||
|
||||
如果两个毫不相关的类出现重复代码,则使用 Extract Class 方法将重复代码提取到一个独立类中。
|
||||
|
||||
## 2. Long Method(过长函数)
|
||||
## 2. 过长函数
|
||||
|
||||
> Long Method
|
||||
|
||||
函数应该尽可能小,因为小函数具有解释能力、共享能力、选择能力。
|
||||
|
||||
@ -255,111 +259,151 @@ Extract Method 会把很多参数和临时变量都当做参数,可以用 Repl
|
||||
|
||||
条件和循环语句往往也需要提取到新的函数中。
|
||||
|
||||
## 3. Large Class(过大的类)
|
||||
## 3. 过大的类
|
||||
|
||||
> Large Class
|
||||
|
||||
应该尽可能让一个类只做一件事,而过大的类做了过多事情,需要使用 Extract Class 或 Extract Subclass。
|
||||
|
||||
先确定客户端如何使用该类,然后运用 Extract Interface 为每一种使用方式提取出一个接口。
|
||||
|
||||
## 4. Long Parameter List(过长的参数列表)
|
||||
## 4. 过长的参数列表
|
||||
|
||||
> Long Parameter List
|
||||
|
||||
太长的参数列表往往会造成前后不一致,不易使用。
|
||||
|
||||
面向对象程序中,函数所需要的数据通常内在宿主类中找到。
|
||||
|
||||
## 5. Divergent Change(发散式变化)
|
||||
## 5. 发散式变化
|
||||
|
||||
> Divergent Change
|
||||
|
||||
设计原则:一个类应该只有一个引起改变的原因。也就是说,针对某一外界变化所有相应的修改,都只应该发生在单一类中。
|
||||
|
||||
针对某种原因的变化,使用 Extract Class 将它提炼到一个类中。
|
||||
|
||||
## 6. Shotgun Surgery(散弹式修改)
|
||||
## 6. 散弹式修改
|
||||
|
||||
> Shotgun Surgery
|
||||
|
||||
一个变化引起多个类修改;
|
||||
|
||||
使用 Move Method 和 Move Field 把所有需要修改的代码放到同一个类中。
|
||||
|
||||
## 7. Feature Envy(依恋情结)
|
||||
## 7. 依恋情结
|
||||
|
||||
> Feature Envy
|
||||
|
||||
一个函数对某个类的兴趣高于对自己所处类的兴趣,通常是过多访问其它类的数据,
|
||||
|
||||
使用 Move Method 将它移到该去的地方,如果对多个类都有 Feature Envy,先用 Extract Method 提取出多个函数。
|
||||
|
||||
## 8. Data Clumps(数据泥团)
|
||||
## 8. 数据泥团
|
||||
|
||||
> Data Clumps
|
||||
|
||||
有些数据经常一起出现,比如两个类具有相同的字段、许多函数有相同的参数,这些绑定在一起出现的数据应该拥有属于它们自己的对象。
|
||||
|
||||
使用 Extract Class 将它们放在一起。
|
||||
|
||||
## 9. Primitive Obsession(基本类型偏执)
|
||||
## 9. 基本类型偏执
|
||||
|
||||
> Primitive Obsession
|
||||
|
||||
使用类往往比使用基本类型更好,使用 Replace Data Value with Object 将数据值替换为对象。
|
||||
|
||||
## 10. Switch Statements(switch 惊悚现身)
|
||||
## 10. switch 惊悚现身
|
||||
|
||||
> Switch Statements
|
||||
|
||||
具体参见第一章的案例。
|
||||
|
||||
## 11. Parallel Inheritance Hierarchies(平行继承体系)
|
||||
## 11. 平行继承体系
|
||||
|
||||
> Parallel Inheritance Hierarchies
|
||||
|
||||
每当为某个类增加一个子类,必须也为另一个类相应增加一个子类。
|
||||
|
||||
这种结果会带来一些重复性,消除重复性的一般策略:让一个继承体系的实例引用另一个继承体系的实例。
|
||||
|
||||
## 12. Lazy Class(冗余类)
|
||||
## 12. 冗余类
|
||||
|
||||
> Lazy Class
|
||||
|
||||
如果一个类没有做足够多的工作,就应该消失。
|
||||
|
||||
## 13. Speculative Generality(夸夸其谈未来性)
|
||||
## 13. 夸夸其谈未来性
|
||||
|
||||
> Speculative Generality
|
||||
|
||||
有些内容是用来处理未来可能发生的变化,但是往往会造成系统难以理解和维护,并且预测未来可能发生的改变很可能和最开始的设想相反。因此,如果不是必要,就不要这么做。
|
||||
|
||||
## 14. Temporary Field(令人迷惑的暂时字段)
|
||||
## 14. 令人迷惑的暂时字段
|
||||
|
||||
> Temporary Field
|
||||
|
||||
某个字段仅为某种特定情况而设,这样的代码不易理解,因为通常认为对象在所有时候都需要它的所有字段。
|
||||
|
||||
把这种字段和特定情况的处理操作使用 Extract Class 提炼到一个独立类中。
|
||||
|
||||
## 15. Message Chains(过度耦合的消息链)
|
||||
## 15. 过度耦合的消息链
|
||||
|
||||
> Message Chains
|
||||
|
||||
一个对象请求另一个对象,然后再向后者请求另一个对象,然后...,这就是消息链。采用这种方式,意味着客户代码将与对象间的关系紧密耦合。
|
||||
|
||||
改用函数链,用函数委托另一个对象来处理。
|
||||
|
||||
## 16. Middle Man(中间人)
|
||||
## 16. 中间人
|
||||
|
||||
> Middle Man
|
||||
|
||||
中间人负责处理委托给它的操作,如果一个类中有过多的函数都委托给其它类,那就是过度运用委托,应当 Remove Middle Man,直接与负责的对象打交道。
|
||||
|
||||
## 17. Inappropriate Intimacy(狎昵关系)
|
||||
## 17. 狎昵关系
|
||||
|
||||
> Inappropriate Intimacy
|
||||
|
||||
两个类多于亲密,花费太多时间去探讨彼此的 private 成分。
|
||||
|
||||
## 18. Alernative Classes with Different Interfaces(异曲同工的类)
|
||||
## 18. 异曲同工的类
|
||||
|
||||
> Alernative Classes with Different Interfaces
|
||||
|
||||
两个函数做同一件事,却有着不同的签名。
|
||||
|
||||
使用 Rename Method 根据它们的用途重新命名。
|
||||
|
||||
## 19. Incomplete Library Class(不完美的类库)
|
||||
## 19. 不完美的类库
|
||||
|
||||
> Incomplete Library Class
|
||||
|
||||
类库的设计者不可能设计出完美的类库,当我们需要对类库进行一些修改时,可以使用以下两种方法:如果只是修改一两个函数,使用 Introduce Foreign Method;如果要添加一大堆额外行为,使用 Introduce Local Extension。
|
||||
|
||||
## 20. Data Class(幼稚的数据类)
|
||||
## 20. 幼稚的数据类
|
||||
|
||||
> Data Class
|
||||
|
||||
它只拥有一些数据字段,以及用于访问这些字段的函数,除此之外一无长物。
|
||||
|
||||
找出字段使用的地方,然后把相应的操作移到 Data Class 中。
|
||||
|
||||
## 21. Refused Bequest(被拒绝的馈赠)
|
||||
## 21. 被拒绝的馈赠
|
||||
|
||||
> Refused Bequest
|
||||
|
||||
子类不想继承超类的所有函数和数据。
|
||||
|
||||
为子类新建一个兄弟类,不需要的函数或数据使用 Push Down Method 和 Push Down Field 下推给那个兄弟。
|
||||
|
||||
## 22. Comments(过多的注释)
|
||||
## 22. 过多的注释
|
||||
|
||||
> Comments
|
||||
|
||||
使用 Extract Method 提炼出需要注释的部分,然后用函数名来解释函数的行为。
|
||||
|
||||
# 构筑测试体系
|
||||
# 四、构筑测试体系
|
||||
|
||||
Java 可以使用 Junit 进行单元测试。
|
||||
|
||||
@ -371,7 +415,7 @@ Java 可以使用 Junit 进行单元测试。
|
||||
|
||||
应当集中测试可能出错的边界条件。
|
||||
|
||||
# 重新组织函数
|
||||
# 五、重新组织函数
|
||||
|
||||
## 1. Extract Method(提炼函数)
|
||||
|
||||
@ -473,7 +517,7 @@ int discount (int inputVal, int quentity, int yearToDate){
|
||||
|
||||
## 9. Subsititute Algorithn(替换算法)
|
||||
|
||||
# 在对象之间搬移特性
|
||||
# 六、在对象之间搬移特性
|
||||
|
||||
## 1. Move Method(搬移函数)
|
||||
|
||||
@ -547,7 +591,7 @@ Hide Delegate 有很大好处,但是它的代价是:每当客户要使用受
|
||||
|
||||
和 Introduce Foreign Method 目的一样,但是 Introduce Local Extension 通过建立新的类来实现。有两种方式:子类或者包装类,子类就是通过继承实现,包装类就是通过组合实现。
|
||||
|
||||
# 重新组织数据
|
||||
# 七、重新组织数据
|
||||
|
||||
## 1. Self Encapsulate Field(自封装字段)
|
||||
|
||||
@ -658,7 +702,7 @@ public 字段应当改为 private,并提供相应的访问函数。
|
||||
|
||||
<div align="center"> <img src="../pics//f2e0cee9-ecdc-4a96-853f-d9f6a1ad6ad1.jpg"/> </div><br>
|
||||
|
||||
# 简化条件表达式
|
||||
# 八、简化条件表达式
|
||||
|
||||
## 1. Decompose Conditional(分解条件表达式)
|
||||
|
||||
@ -805,7 +849,7 @@ double getExpenseLimit() {
|
||||
}
|
||||
```
|
||||
|
||||
# 简化函数调用
|
||||
# 九、简化函数调用
|
||||
|
||||
## 1. Rename Method(函数改名)
|
||||
|
||||
@ -980,7 +1024,7 @@ double getValueForPeriod(int periodNumber) {
|
||||
return values[periodNumber];
|
||||
```
|
||||
|
||||
# 处理概括关系
|
||||
# 十、处理概括关系
|
||||
|
||||
## 1. Pull Up Field(字段上移)
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user