diff --git a/notes/设计模式.md b/notes/设计模式.md index cf6e5f68..d54fe89f 100644 --- a/notes/设计模式.md +++ b/notes/设计模式.md @@ -58,11 +58,11 @@ ### 实现 -**(一)懒汉式-线程不安全** +(一)懒汉式-线程不安全 以下实现中,私有静态变量 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,7 +81,7 @@ public class Singleton { } ``` -**(二)懒汉式-线程安全** +(二)懒汉式-线程安全 只需要对 getUniqueInstance() 方法加锁,那么在一个时间点只能有一个线程能够进入该方法,从而避免了对 uniqueInstance 进行多次实例化的问题。 @@ -96,7 +96,7 @@ public static synchronized Singleton getUniqueInstance() { } ``` -**(三)饿汉式-线程安全** +(三)饿汉式-线程安全 线程不安全问题主要是由于 uniqueInstance 被实例化了多次,如果 uniqueInstance 采用直接实例化的话,就不会被实例化多次,也就不会产生线程不安全问题。但是直接实例化的方式也丢失了延迟实例化带来的节约资源的优势。 @@ -104,7 +104,7 @@ public static synchronized Singleton getUniqueInstance() { private static Singleton uniqueInstance = new Singleton(); ``` -**(四)双重校验锁-线程安全** +(四)双重校验锁-线程安全 uniqueInstance 只需要被实例化一次,之后就可以直接使用了。加锁操作只需要对实例化那部分的代码进行。也就是说,只有当 uniqueInstance 没有被实例化时,才需要进行加锁。 @@ -143,17 +143,17 @@ if (uniqueInstance == null) { uniqueInstance 采用 volatile 关键字修饰也是很有必要的。uniqueInstance = new Singleton(); 这段代码其实是分为三步执行。 -1. 分配内存空间。 -2. 初始化对象。 -3. 将 uniqueInstance 指向分配的内存地址。 +1. 分配内存空间 +2. 初始化对象 +3. 将 uniqueInstance 指向分配的内存地址 但是由于 JVM 具有指令重排的特性,有可能执行顺序变为了 1>3>2,这在单线程情况下自然是没有问题。但如果是多线程下,有可能获得是一个还没有被初始化的实例,以致于程序出错。 使用 volatile 可以禁止 JVM 的指令重排,保证在多线程环境下也能正常运行。 -**(五)枚举实现** +(五)枚举实现 -这是单例模式的最佳实践,它实现简单,并且在复杂的序列化或者反射攻击的时候,能够防止实例化多次。 +这是单例模式的最佳实践,它实现简单,并且在面对复杂的序列化或者反射攻击的时候,能够防止实例化多次。 ```java public enum Singleton { @@ -164,7 +164,7 @@ public enum Singleton { 考虑以下单例模式的实现,该 Singleton 在每次序列化的时候都会创建一个新的实例,为了保证只创建一个实例,必须声明所有字段都是 transient,并且提供一个 readResolve() 方法。 ```java -public class Singleton implements Serializable{ +public class Singleton implements Serializable { private static Singleton uniqueInstance; @@ -283,7 +283,7 @@ public class Client { 在简单工厂中,创建对象的是另一个类,而在工厂方法中,是由子类来创建对象。 -下图中,Factory 有一个 doSomethind() 方法,这个方法需要用到一组产品对象,这组产品对象由 factoryMethod() 方法创建。该方法是抽象的,需要由子类去实现。 +下图中,Factory 有一个 doSomethind() 方法,这个方法需要用到一个产品对象,这个产品对象由 factoryMethod() 方法创建。该方法是抽象的,需要由子类去实现。

@@ -341,15 +341,15 @@ public class ConcreteFactory2 extends Factory { ### 类图 -

+抽象工厂模式创建的是对象家族,也就是很多对象而不是一个对象,并且这些对象是相关的,也就是说必须一起创建出来。而工厂方法模式只是用于创建一个对象,这和抽象工厂模式有很大不同。 -抽象工厂模式创建的是对象家族,也就是很多对象而不是一个对象,并且这些对象是相关的,也就是说必须一起创建出来。而工厂模式只是用于创建一个对象,这和抽象工厂模式有很大不同。 - -抽象工厂模式用到了工厂模式来创建单一对象,AbstractFactory 中的 createProductA() 和 createProductB() 方法都是让子类来实现,这两个方法单独来看就是在创建一个对象,这符合工厂模式的定义。 +抽象工厂模式用到了工厂方法模式来创建单一对象,AbstractFactory 中的 createProductA() 和 createProductB() 方法都是让子类来实现,这两个方法单独来看就是在创建一个对象,这符合工厂方法模式的定义。 至于创建对象的家族这一概念是在 Client 体现,Client 要通过 AbstractFactory 同时调用两个方法来创建出两个对象,在这里这两个对象就有很大的相关性,Client 需要同时创建出这两个对象。 -从高层次来看,抽象工厂使用了组合,即 Cilent 组合了 AbstractFactory,而工厂模式使用了继承。 +从高层次来看,抽象工厂使用了组合,即 Cilent 组合了 AbstractFactory,而工厂方法模式使用了继承。 + +

### 代码实现 @@ -584,7 +584,7 @@ abc ### 意图 -使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。 +使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链发送该请求,直到有一个对象处理它为止。 ### 类图