设计模式
单例模式(Singletion)
Intent
确保一个类只有一个实例,并提供该实例的全局访问点
Class Diagram
使用一个私有构造函数、一个私有静态变量以及一个公有静态函数来实现。
私有构造函数保证了不能通过构造函数来创建对象实例,只能通过公有静态函数返回唯一的私有静态变量。
双重校验锁-线程安全
1 | public class Singleton{ |
Q.为什么在第一个判断语句内加锁?
A.因为这也能保证 uniqueInstance 被实例化以后,调用该方法的线程可以直接退出,不用等待。
Q.为什么要有两个 if 判断语句?
A.如果只使用了一个 if 语句。在 uniqueInstance == null 的情况下,如果两个线程都执行了 if 语句,那么两个线程都会进入 if 语句块内。虽然在 if 语句块内有加锁操作,但是两个线程都会执行 uniqueInstance = new Singleton();
这条语句,只是先后的问题,那么就会进行两次实例化。因此必须使用双重校验锁,也就是需要使用两个 if 语句:第一个 if 语句用来避免 uniqueInstance 已经被实例化之后的加锁操作,而第二个 if 语句进行了加锁,所以只能有一个线程进入,就不会出 uniqueInstance == null 时两个线程同时进行实例化操作。
1 | if(uniqueInstance == null){ |
Q.unique Instance 为什么使用 volatile 关键字修饰?
A. 因为unique Instance = new Singleton(); 这段代码分三步执行:
- 为 uniqueInstance 分配内存空间
- 初始化 uniqueInstance
- 将 uniqueInstance 指向分配的内存地址
但是由于 JVM 具有指令重排的特性,执行顺序有可能变成 1>3>2。指令重排在单线程环境下不会出现问题,但是在多线程环境下会导致一个线程获得还没有初始化的实例。例如,线程 T1 执行了 1 和 3,此时 T2 调用 getUniqueInstance() 后发现 uniqueInstance 不为空,因此返回 uniqueInstance,但此时 uniqueInstance 还未被初始化。
使用 volatile 可以禁止 JVM 的指令重排,保证在多线程环境下也能正常运行。
简单工厂(Simple Factory)
Intent
在创建一个对象时不向客户暴露内部细节,并提供一个创建对象的接口。
Class Diagram
简单工厂把实例化的操作单独放在一个类中,这个类就成为简单工厂类,让简单工厂类来决定应该用那个具体子类来实例化。
==这样做能把客户类和具体子类的实现解耦==,客户类不再需要知道有哪些子类以及应当实例化哪个子类。客户类往往有多个,如果不使用简单工厂,那么所有的客户类都要知道所有子类的细节。而且一旦子类发生改变,例如增加子类,那么所有的客户类都要进行修改。
Implementation
1 | public interface Product { |
以下的 Client 类包含了实例化的代码,这是一种错误的实现。如果在客户类中存在这种实例化代码,就需要考虑将代码放到简单工厂中。
1 | public class Client { |
以下的 SimpleFactory 是简单工厂实现,它被所有需要进行实例化的客户类调用。
1 | public class SimpleFactory { |
观察者(Observer)
Intent
定义对象之间的一对多依赖,当一个对象状态改变时,它的所有依赖都会受到通知并且自动更新状态。
主题(Subject)是被观察的对象,而其所有依赖者(Observer)称为观察者。
[#](https://cyc2018.xyz/其它/设计模式/设计模式 - 观察者.html#class-diagram)Class Diagram
主题(Subject)具有注册和移除观察者、并通知所有观察者的功能,主题是通过维护一张观察者列表来实现这些操作的。
观察者(Observer)的注册功能需要调用主题的 registerObserver() 方法。
Implementation
天气数据布告板会在天气信息发生改变时更新其内容,布告板有多个,并且在将来会继续增加。
1 | public interface Subject{ |
1 | public class WeatherData implements Subject{ |
1 | public interface Observer{ |
1 | public class StatisticsDisplay implements Observer { public StatisticsDisplay(Subject weatherData) { weatherData.registerObserver(this); } public void update(float temp, float humidity, float pressure) { System.out.println("StatisticsDisplay.update: " + temp + " " + humidity + " " + pressure); }} |
1 | public class CurrentConditionsDisplay implements Observer { public CurrentConditionsDisplay(Subject weatherData) { weatherData.registerObserver(this); } public void update(float temp, float humidity, float pressure) { System.out.println("CurrentConditionsDisplay.update: " + temp + " " + humidity + " " + pressure); }} |
1 | public class WeatherStation { public static void main(String[] args) { WeatherData weatherData = new WeatherData(); CurrentConditionsDisplay currentConditionsDisplay = new CurrentConditionsDisplay(weatherData); StatisticsDisplay statisticsDisplay = new StatisticsDisplay(weatherData); weatherData.setMeasurements(0, 0, 0); weatherData.setMeasurements(1, 1, 1); }} |