## 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专题五:持久化