设计模式

单例模式(Singletion)


Intent

确保一个类只有一个实例,并提供该实例的全局访问点

Class Diagram

使用一个私有构造函数、一个私有静态变量以及一个公有静态函数来实现。

私有构造函数保证了不能通过构造函数来创建对象实例,只能通过公有静态函数返回唯一的私有静态变量。

img

双重校验锁-线程安全

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class Singleton{
private volatile static Singleton uniqueInstance;

private Singletion(){

}

public static Singleton getUniqurInstance(){
if(uniqueInstance == null){
synchronized(Singleton.class){
if(uniqueInstance == null){
uniqueInstance = new Singleton;
}
}
}
return uniqueInstance;
}
}
Q.为什么在第一个判断语句内加锁?
A.因为这也能保证 uniqueInstance 被实例化以后,调用该方法的线程可以直接退出,不用等待。
Q.为什么要有两个 if 判断语句?
A.如果只使用了一个 if 语句。在 uniqueInstance == null 的情况下,如果两个线程都执行了 if 语句,那么两个线程都会进入 if 语句块内。虽然在 if 语句块内有加锁操作,但是两个线程都会执行 uniqueInstance = new Singleton(); 这条语句,只是先后的问题,那么就会进行两次实例化。因此必须使用双重校验锁,也就是需要使用两个 if 语句:第一个 if 语句用来避免 uniqueInstance 已经被实例化之后的加锁操作,而第二个 if 语句进行了加锁,所以只能有一个线程进入,就不会出 uniqueInstance == null 时两个线程同时进行实例化操作。
1
2
3
4
5
if(uniqueInstance == null){
synchronized(Singleton.class){
uniqueInstance = new Singleton();
}
}
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

简单工厂把实例化的操作单独放在一个类中,这个类就成为简单工厂类,让简单工厂类来决定应该用那个具体子类来实例化。

==这样做能把客户类和具体子类的实现解耦==,客户类不再需要知道有哪些子类以及应当实例化哪个子类。客户类往往有多个,如果不使用简单工厂,那么所有的客户类都要知道所有子类的细节。而且一旦子类发生改变,例如增加子类,那么所有的客户类都要进行修改。

img

Implementation

1
2
3
4
5
6
7
8
public interface Product {
}
public class ConcreteProduct implements Product {
}
public class ConcreteProduct1 implements Product {
}
public class ConcreteProduct2 implements Product {
}

以下的 Client 类包含了实例化的代码,这是一种错误的实现。如果在客户类中存在这种实例化代码,就需要考虑将代码放到简单工厂中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Client {

public static void main(String[] args) {
int type = 1;
Product product;
if (type == 1) {
product = new ConcreteProduct1();
} else if (type == 2) {
product = new ConcreteProduct2();
} else {
product = new ConcreteProduct();
}
// do something with the product
}
}

以下的 SimpleFactory 是简单工厂实现,它被所有需要进行实例化的客户类调用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class SimpleFactory {

public Product createProduct(int type) {
if (type == 1) {
return new ConcreteProduct1();
} else if (type == 2) {
return new ConcreteProduct2();
}
return new ConcreteProduct();
}
}
public class Client {

public static void main(String[] args) {
SimpleFactory simpleFactory = new SimpleFactory();
Product product = simpleFactory.createProduct(1);
// do something with the product
}
}

观察者(Observer)


Intent

定义对象之间的一对多依赖,当一个对象状态改变时,它的所有依赖都会受到通知并且自动更新状态。

主题(Subject)是被观察的对象,而其所有依赖者(Observer)称为观察者。

img

[#](https://cyc2018.xyz/其它/设计模式/设计模式 - 观察者.html#class-diagram)Class Diagram

主题(Subject)具有注册和移除观察者、并通知所有观察者的功能,主题是通过维护一张观察者列表来实现这些操作的。

观察者(Observer)的注册功能需要调用主题的 registerObserver() 方法。

img

Implementation

天气数据布告板会在天气信息发生改变时更新其内容,布告板有多个,并且在将来会继续增加。

img

1
2
3
4
5
6
7
public interface Subject{
void registerObserver(Observer o);

void removeObserver(Observer o);

void notifyObserver();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
public class WeatherData implements Subject{
private List<Observer> observer;
private float temperature;
private float humidity;
private float pressure;

public WeatherData(){
observers = new ArrayList<>();
}

public void setMeasurements(float temperature, float humidity, float pressure){
this.temperature = temperature;
this.humidity = humidity;
this.pressure = pressure;
notifyObserver();
}

@Override
public void registerObserver(Observer o) {
observers.add(o);
}

@Override
public void removeObserver(Observer o) {
int i = observers.indexOf(o);
if (i >= 0) {
observers.remove(i);
}
}

@Override
public void notifyObserver() {
for (Observer o : observers) {
o.update(temperature, humidity, pressure);
}
}
}
1
2
3
public interface Observer{
void update(float temp,float humidity,float pressure);
}
1
public class StatisticsDisplay implements Observer {    public StatisticsDisplay(Subject weatherData) {        weatherData.registerObserver(this);    }    @Override    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);    }    @Override    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);    }}