## Java專題二十:設計模式
[TOC]
### 20.1. 創建型模式
#### 20.1.1. 單例模式(Singleton Pattern)
> 確保一個對象只有一個實例對象,并且提供了一個全局訪問方法該對象的方法
- 延遲創建實例對象,但`getInstance`方法在多線程中可能會創建2個實例對象,當2個線程執行到`uniqueInstance == null`語句判斷`uniqueInstance`是空的,然后都創建了該對象,造成了創建2個實例對象的問題
~~~
public class Singleton {
private static Singleton uniqueInstance;
private Singleton() {}
public static Singleton getInstance() {
if (uniqueInstance == null) {
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
}
~~~
- 延遲創建實例對象,且使用同步`synchronized`對方法加鎖實現同步,但直接對`getInstance`方法加鎖的成本太高,因此有了下面的早期創建實例對象和double-checking鎖方法創建實例對象
~~~
public class Singleton {
private static Singleton uniqueInstance;
private Singleton() {}
public static synchronized Singleton getInstance() {
if (uniqueInstance == null) {
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
}
~~~
- 早期創建實例對象,不需要使用同步方法
~~~
public class Singleton {
private static Singleton uniqueInstance = new Singleton();
private Singleton() {}
public static Singleton getInstance() {
return uniqueInstance;
}
}
~~~
- double-checking鎖方法創建實例對象
~~~
public class Singleton {
private volatile static Singleton uniqueInstance;
private Singleton() {}
public static Singleton getInstance() {
if (uniqueInstance == null) {
synchronized (Singleton.class) {
if (uniqueInstance == null) {
uniqueInstance = new Singleton();
}
}
}
return uniqueInstance;
}
}
~~~
示例:`java.lang.Runtime`就是使用的早期創建實例對象的方法實現單例模式
~~~
public class Runtime {
private static Runtime currentRuntime = new Runtime();
public static Runtime getRuntime() {
return currentRuntime;
}
private Runtime() {}
}
~~~
#### 20.1.2. 建造者模式(Builder Pattern)
> 用于構建成一個復雜的對象,將構建與表示分離
示例:`java.lang.StringBuffer`和`java.lang.StringBuilder`
~~~
public final class StringBuilder {
public StringBuilder append(String str) {
super.append(str);
return this;
}
}
~~~
#### 20.1.3. 原型模式(Prototype Pattern)
> 用于創建對象的復制本,一般通過實現接口`Cloneable`,并重寫`clone`方法
示例:參考 [Java專題十九:淺拷貝與深拷貝](Java專題十九:淺拷貝與深拷貝.md)
#### 20.1.4. 工廠模式(Factory Pattern)
> 用于創建多個實例對象
示例:`java.util.concurrent.ThreadFactory`用來創建新的線程
### 20.2. 結構型模式
#### 20.2.1. 裝飾器模式(Decorator Pattern)
> 在不改變現有類的前提下,為現有類添加新的功能
示例:在IO包中,`InputStream`相當于現有類,`FileterInputStream`相當于抽象裝飾類,`BufferedInputStream`、`DataInputStream`為具體實現裝飾類,這些具體實現裝飾類在`InputStream`的基礎增加了新功能
#### 20.2.2. 適配器模式(Adapter Pattern)
> 作為2個不兼容接口的橋梁,使他們可以進行相互轉換
示例:`java.io.InputStreamReader`就是一個橋梁,將字節流對象轉換成字符流對象,`InputStreamReader(InputStream in)`
#### 20.2.3. 代理模式(Proxy Pattern)
> 在不修改委托類代碼的情況下,可以為委托類的方法增加一些操作,如方法中實際代碼前后增加日志輸出
示例:參考 [Java專題九(2):動態代理](Java專題九(2):動態代理.md)
### 20.3. 行為型模式
#### 20.3.1. 觀察者模式(Observer Pattern)
> 對象之間存在1對多關系,當被觀察對象發生變化時,會通知所有觀察者
JDK中自帶觀察者設計,`java.util.Observable`和`java.util.Observer`,`Observable`是一個類,代表被觀察對象,`Observer`是一個接口,代表觀察者;使用時被觀察對象繼承`Observable`類,在發生改變時,主動調用`void setChanged()`說明被觀察對象發生改變,然后調用`void notifyObservers(Object arg) `通知觀察者變化的內容
示例:現在我們有一個博主類`Blogger`,和一個粉絲類`Fans`,一個博主有很多個粉絲,當博主發表新的文章時,自動通知粉絲
- 被觀察者:
~~~
import java.text.SimpleDateFormat;
import java.util.*;
public class Blogger extends Observable {
String name;
public Blogger(String name){
this.name = name;
}
public void publish(String message){
this.setChanged();
Map<String,String> map = new HashMap<String, String>();
map.put("name", name);
map.put("message", message);
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String nowTime = df.format(System.currentTimeMillis());
map.put("date", nowTime);
this.notifyObservers(map);
}
}
~~~
- 觀察者
~~~
import java.util.HashMap;
import java.util.Map;
import java.util.Observable;
import java.util.Observer;
public class Fans implements Observer{
String name;
public Fans(String name){
this.name = name;
}
@Override
public void update(Observable o, Object arg) {
Map<String,String> map = (HashMap)arg;
System.out.println(name+ "=>observe:"+map);
}
}
~~~
- 驗證代碼
~~~
Blogger blogger = new Blogger("ibu");
Fans fan0 = new Fans("fan0");
Fans fan1 = new Fans("fan1");
blogger.addObserver(fan0);
blogger.addObserver(fan1);
blogger.publish("Java設計模式");
~~~
名為`ibu`的博主發布了一篇名為`Java設計模式`,它的2個粉絲,`fan0`和`fan1`都觀察到了這個變化。
~~~
fan1=>observe:{date=2019-12-03 20:55:25, name=ibu, message=Java設計模式}
fan0=>observe:{date=2019-12-03 20:55:25, name=ibu, message=Java設計模式}
~~~
#### 20.3.2. 解釋器模式(Interpreter Pattern)
> 定義了一個語言的語法,然后可以解析
示例:`java.util.regex.Pattern`定義了正則表達式的語法,詳細可參考正則 [Java專題七:正則表達式](Java專題七:正則表達式.md)
- JavaCook
- Java專題零:類的繼承
- Java專題一:數據類型
- Java專題二:相等與比較
- Java專題三:集合
- Java專題四:異常
- Java專題五:遍歷與迭代
- Java專題六:運算符
- Java專題七:正則表達式
- Java專題八:泛型
- Java專題九:反射
- Java專題九(1):反射
- Java專題九(2):動態代理
- Java專題十:日期與時間
- Java專題十一:IO與NIO
- Java專題十一(1):IO
- Java專題十一(2):NIO
- Java專題十二:網絡
- Java專題十三:并發編程
- Java專題十三(1):線程與線程池
- Java專題十三(2):線程安全與同步
- Java專題十三(3):內存模型、volatile、ThreadLocal
- Java專題十四:JDBC
- Java專題十五:日志
- Java專題十六:定時任務
- Java專題十七:JavaMail
- Java專題十八:注解
- Java專題十九:淺拷貝與深拷貝
- Java專題二十:設計模式
- Java專題二十一:序列化與反序列化
- 附加專題一:MySQL
- MySQL專題零:簡介
- MySQL專題一:安裝與連接
- MySQL專題二:DDL與DML語法
- MySQL專題三:工作原理
- MySQL專題四:InnoDB存儲引擎
- MySQL專題五:sql優化
- MySQL專題六:數據類型
- 附加專題二:Mybatis
- Mybatis專題零:簡介
- Mybatis專題一:配置文件
- Mybatis專題二:映射文件
- Mybatis專題三:動態SQL
- Mybatis專題四:源碼解析
- 附加專題三:Web編程
- Web專題零:HTTP協議
- Web專題一:Servlet
- Web專題二:Cookie與Session
- 附加專題四:Redis
- Redis專題一:數據類型
- Redis專題二:事務
- Redis專題三:key的過期
- Redis專題四:消息隊列
- Redis專題五:持久化