設計模式筆記——GoF設計模式匯總


目錄

· 總述

    · 記憶

    · 效果

    · 面向對象設計原則

· 創建型模式

    · 單例模式(Singleton)

        · 效果

        · 分類

        · 代碼(餓漢式)

        · 代碼(懶漢式)

        · 代碼(雙重檢測鎖式)

        · 代碼(靜態內部類式)

        · 代碼(枚舉單例)

        · 代碼(使用反射的破解與防御)

        · 代碼(使用序列化的破解與防御)

        · 應用場景

    · 工廠模式

        · 效果

        · 分類

        · 代碼(簡單工廠)

        · 代碼(工廠方法)

        · 代碼(抽象工廠)

        · 應用場景

    · 構建者模式(Builder)

        · 效果

        · 代碼

        · 應用場景

    · 原型模式(Prototype)

        · 效果

        · 核心角色

        · 代碼(淺克隆)

        · 代碼(基於JDK的深克隆)

        · 代碼(基於序列化的深克隆)

        · 應用場景

· 結構型模式

    · 適配器模式(Adapter)

        · 效果

        · 核心角色

        · 分類

        · 代碼(使用繼承)

        · 代碼(使用關聯)

        · 應用場景

    · 代理模式(Proxy)

        · 效果

        · 核心角色

        · 分類

        · 代碼(靜態代理)

        · 代碼(動態代理)

        · 應用場景

    · 橋接模式(Bridge)

        · 效果

        · 代碼

        · 應用場景

    · 組合模式(Composite)

        · 效果

        · 核心角色

        · 代碼

        · 代碼(殺毒舉例)

        · 應用場景

    · 裝飾器模式(Decorator)

        · 效果

        · 核心角色

        · 代碼

        · 應用場景

    · 外觀模式(Facade)

        · 效果

        · 代碼

        · 應用場景

    · 享元模式(FlyWeight)

        · 效果

        · 核心角色

        · 代碼

        · 應用場景

· 行為型模式

    · 責任鏈模式(Chain of Resposibility)

        · 效果

        · 核心角色

        · 代碼

        · 應用場景

    · 迭代器模式(Iterator)

        · 效果

        · 核心角色

        · 代碼

        · 應用場景

    · 中介者模式(Mediator)

        · 效果

        · 代碼

        · 應用場景

    · 命令模式(Command)

        · 效果

        · 核心角色

        · 代碼

        · 應用場景

    · 解釋器模式(Interpreter)

        · 效果

        · 應用場景

    · 訪問者模式(Visitor)

        · 效果

        · 應用場景

    · 策略模式(Strategy)

        · 效果

        · 代碼

        · 應用場景

    · 模板方法模式(Template Method)

        · 效果

        · 代碼

        · 應用場景

    · 狀態模式(State)

        · 效果

        · 核心角色

        · 代碼

        · 應用場景

    · 觀察者模式(Observer)

        · 效果

        · 代碼

        · 代碼(基於JDK)

        · 應用場景

    · 備忘錄模式(Memento)

        · 效果

        · 核心角色

        · 代碼

        · 應用場景


 

總述

記憶

1. 創建型:sbfp;

2. 結構型:abcdffp;

3. 行為型:iimmccsstov。

效果

1. 所有面向對象設計原則和設計模式都是為了降低代碼耦合度,提高擴展性、復用,手段是“分工”。

2. 類似社會分工,現代社會比原始社會發展得大,也是因為分工。

面向對象設計原則 

首字母

代指

解釋

S

單一功能原則(Single Responsibility Principle)

對象應該僅具有一種單一功能。

O

開閉原則(Open/Closed Principle)

軟件體應該是對於擴展開放的,但是對於修改封閉的。

L

里氏替換原則(Liskov Substitution Principle)

程序中的對象應該是可以在不改變程序正確性的前提下被它的子類所替換的。

I

接口隔離原則(Interface Segregation Principle)

多個特定客戶端接口要好於一個寬泛用途的接口。

D

依賴反轉原則(Dependency Inversion Principle)

一個方法應該遵從依賴於抽象而不是一個實例

創建型模式

單例模式(Singleton)

效果

1. 保證一個類只有一個實例,並且提供一個訪問該實例的全局訪問點。

2. 由於單例模式只生成一個實例,減少了系統性能開銷,當一個對象的產生需要比較多的資源時,如讀取配置、產生其他依賴對象時,則可以通過在應用啟動時直接產生一個單例對象,然后永久駐留內存的方式來解決。

分類

1. 常見實現方式。

    a) 餓漢式:線程安全,調用效率高,不能延時加載。

    b) 懶漢式:線程安全,調用效率不高,可延時加載。

2. 其他實現方式。

    a) 雙重檢測鎖式:由於JVM底層內部模型原因,偶爾出問題,不建議使用。

    b) 靜態內部類式:線程安全,調用效率高,可延時加載。

    c) 枚舉單例:線程安全,調用效率高,不能延時加載。

3. 選擇方法。

    a) 單例對象占用資源少,不需要延時加載:枚舉式好於餓漢式;

    b) 單例對象占用資源多,需要延時加載:靜態內部類好於懶漢式。

代碼(餓漢式)

1. Singleton.java

 1 public class Singleton {
 2 
 3     // 類初始化時立即創建對象
 4     private static final Singleton instance = new Singleton();
 5     
 6     // 私有化構造器
 7     private Singleton() {
 8         if (instance != null) {
 9             throw new RuntimeException();
10         }
11     }
12     
13     public static Singleton getInstance() {
14         return instance;
15     }
16     
17 }
View Code

2. Client.java

1 public class Client {
2 
3     public static void main(String[] args) {
4         Singleton singleton1 = Singleton.getInstance();
5         Singleton singleton2 = Singleton.getInstance();
6         System.out.println(singleton1 == singleton2);
7     }
8 
9 }
View Code

代碼(懶漢式)

1. Singleton.java

 1 public class Singleton {
 2 
 3     private static Singleton instance;
 4     
 5     // 私有化構造器
 6     private Singleton() {
 7     }
 8     
 9     // 同步方法
10     public static synchronized Singleton getInstance() {
11         if (instance == null) {
12             // 延時加載
13             instance = new Singleton();
14         }
15         return instance;
16     }
17     
18 }
View Code

2. Client.java

1 public class Client {
2 
3     public static void main(String[] args) {
4         Singleton singleton1 = Singleton.getInstance();
5         Singleton singleton2 = Singleton.getInstance();
6         System.out.println(singleton1 == singleton2);
7     }
8 
9 }
View Code

代碼(雙重檢測鎖式)

1. Singleton.java

 1 public class Singleton {
 2 
 3     private static volatile Singleton instance;
 4     
 5     // 私有化構造器
 6     private Singleton() {
 7     }
 8     
 9     public static Singleton getInstance() {
10         if (instance == null) {
11             // 第一次創建時同步
12             synchronized (Singleton.class) {
13                 if (instance == null) {
14                     // 延時加載
15                     instance = new Singleton();
16                 }
17             }
18         }
19         return instance;
20     }
21     
22 }
View Code

2. Client.java

1 public class Client {
2 
3     public static void main(String[] args) {
4         Singleton singleton1 = Singleton.getInstance();
5         Singleton singleton2 = Singleton.getInstance();
6         System.out.println(singleton1 == singleton2);
7     }
8 
9 }
View Code

代碼(靜態內部類式)

1. Singleton.java

 1 public class Singleton {
 2     
 3     // 初始化外部類時不會立即初始化內部類
 4     private static class SingletonInstance {
 5         private static final Singleton instance = new Singleton();
 6     }
 7 
 8     // 私有化構造器
 9     private Singleton() {
10     }
11     
12     public static Singleton getInstance() {
13         return SingletonInstance.instance;
14     }
15     
16 }
View Code

2. Client.java

1 public class Client {
2 
3     public static void main(String[] args) {
4         Singleton singleton1 = Singleton.getInstance();
5         Singleton singleton2 = Singleton.getInstance();
6         System.out.println(singleton1 == singleton2);
7     }
8 
9 }
View Code

代碼(枚舉單例)

1. Singleton.java

 1 public enum Singleton {
 2     
 3     // 枚舉本身就是單例
 4     INSTANCE;
 5     
 6     // 添加需要的方法
 7     public void method() {
 8     }
 9     
10 }
View Code

2. Client.java

1 public class Client {
2 
3     public static void main(String[] args) {
4         Singleton singleton1 = Singleton.INSTANCE;
5         Singleton singleton2 = Singleton.INSTANCE;
6         System.out.println(singleton1 == singleton2);
7     }
8 
9 }
View Code

代碼(使用反射的破解與防御)

1. Singleton.java

 1 public class Singleton {
 2 
 3     // 類初始化時立即創建對象
 4     private static final Singleton instance = new Singleton();
 5     
 6     // 私有化構造器
 7     private Singleton() {
 8         // 防御:再次創建時拋出異常
 9         if (instance != null) {
10             throw new RuntimeException();
11         }
12     }
13     
14     public static Singleton getInstance() {
15         return instance;
16     }
17     
18 }
View Code

2. Client.java

 1 import java.lang.reflect.Constructor;
 2 
 3 public class Client {
 4 
 5     public static void main(String[] args) throws Exception {
 6         Singleton singleton1 = Singleton.getInstance();
 7         
 8         Class<Singleton> clazz = Singleton.class;
 9         Constructor<Singleton> constructor = clazz.getDeclaredConstructor();
10         constructor.setAccessible(true);
11         Singleton singleton2 = constructor.newInstance();
12         System.out.println(singleton1 == singleton2);
13     }
14 
15 }
View Code

代碼(使用序列化的破解與防御)

1. Singleton.java

 1 import java.io.Serializable;
 2 
 3 public class Singleton implements Serializable {
 4 
 5     private static final long serialVersionUID = -3230831923851678463L;
 6     
 7     // 類初始化時立即創建對象
 8     private static final Singleton instance = new Singleton();
 9     
10     // 私有化構造器
11     private Singleton() {
12     }
13     
14     public static Singleton getInstance() {
15         return instance;
16     }
17     
18     // 防御:反序列化時,直接返回該方法的返回值
19     private Object readResolve() {
20         return instance;
21     }
22     
23 }
View Code

2. Client.java

 1 import java.io.File;
 2 import java.io.FileInputStream;
 3 import java.io.FileOutputStream;
 4 import java.io.ObjectInputStream;
 5 import java.io.ObjectOutputStream;
 6 
 7 public class Client {
 8 
 9     public static void main(String[] args) throws Exception {
10         Singleton singleton1 = Singleton.getInstance();
11         
12         File tempFile = new File("D:/test");
13         ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(tempFile));
14         oos.writeObject(singleton1);
15         oos.close();
16         ObjectInputStream ios = new ObjectInputStream(new FileInputStream(tempFile));
17         Singleton singleton2 = (Singleton) ios.readObject();
18         ios.close();
19         System.out.println(singleton1 == singleton2);
20         
21     }
22 
23 }
View Code

應用場景

1. Windows的Task Manager(任務管理器)。

2. Windows的Recycle Bin(回收站)。

3. 項目中,讀取配置文件的類,一般只有一個對象,沒必要每次創建。

4. 數據庫連接池。

5. Application是單例的典型應用(Servlet編程)。

6. Spring中,每個Bean默認是單例的。

7. 每個Servlet是單例。

8. Spring MVC中,控制器對象是單例的。

工廠模式

效果

1. 實例化對象,用工廠方法代替new。實現了創建者和調用者的分離。

2. 將選擇實現類、創建對象統一管理和控制,從而將調用者跟我們的實現類解耦。

分類

1. 簡單工廠模式:用來產生同一等級結構中的任意產品。對於增加新的產品,需要修改已有代碼。

2. 工廠方法模式:用來產生同一等級結構中的固定產品。支持增加任意產品。

3. 抽象工廠模式:用來生產不同產品族的全部產品。對於增加新的產品,無能為力;支持增加產品族。

4. 簡單工廠模式效果:

    a) 又稱靜態工廠模式。

    b) 工廠類一般使用靜態方法,通過接收的參數來返回不同的對象實例。

    c) 對於增加新產品只能修改代碼(違反OCP)。

    d) 有兩種實現方式(見代碼)。

5. 工廠方法模式效果:

    a) 避免簡單工廠的缺點,但不完全滿足OCP。

    b) 簡單工廠模式VS.工廠方法模式:

        i. 結構復雜度:顯然簡單工廠模式占優,簡單工廠模式只要一個工廠,而工廠方法模式的工廠類隨着產品類個數增加而增加。

        ii. 代碼復雜度:代碼復雜度與結構復雜度相反,簡單工廠模式的工廠類隨着產品增加需要增加更多方法(代碼),而工廠方法模式每個具體工廠類只完成單一任務,代碼簡單。

        iii. 客戶端編程難度:工廠方法模式雖然滿足了OCP,但客戶端編碼中需要對工廠實例化,而簡單工廠模式的工廠類是一個靜態類。

        iv. 管理上的難度:工廠方法模式需要維護的工廠類過多,而簡單工廠模式只有一個。

    c) 設計理論建議使用工廠方法模式,但實際中一般都用簡單工廠模式。

6. 抽象工廠模式效果:

    a) 用來生產不同產品族的全部產品。對於增加新的產品,無能為力;支持增加產品族。

    b) 抽象工廠模式是工廠方法模式的升級版本,在有多個業務品種、業務分類時,通過抽象工廠模式產生需要的對象時一種非常好的解決方式。

代碼(簡單工廠)

1. Car.java

1 public interface Car {
2 
3     void run();
4     
5 }
View Code

2. Audi.java

1 public class Audi implements Car {
2 
3     @Override
4     public void run() {
5         System.out.println("奧迪在跑!");
6     }
7 
8 }
View Code

3. Byd.java

1 public class Byd implements Car {
2 
3     @Override
4     public void run() {
5         System.out.println("比亞迪在跑!");
6     }
7 
8 }
View Code

4. CarFactory.java

 1 // 一個方法實現
 2 public class CarFactory1 {
 3 
 4     public static Car createCar(String type) {
 5         if ("Audi".equals(type)) {
 6             return new Audi();
 7         }
 8         if ("Byd".equals(type)) {
 9             return new Byd();
10         }
11         return null;
12     }
13     
14 }
View Code

5. CarFactory2.java

 1 // 多個方法實現
 2 public class CarFactory2 {
 3     
 4     public static Car createAudi() {
 5         return new Audi();
 6     }
 7 
 8     public static Car createByd() {
 9         return new Byd();
10     }
11     
12 }
View Code

6. Client.java

 1 public class Client {
 2 
 3     public static void main(String[] args) {
 4         Car car1 = CarFactory1.createCar("Audi");
 5         Car car2 = CarFactory1.createCar("Byd");
 6         car1.run();
 7         car2.run();
 8     }
 9 
10 }
View Code

代碼(工廠方法)

1. Car.java

1 public interface Car {
2 
3     void run();
4     
5 }
View Code

2. CarFactory.java

1 public interface CarFactory {
2 
3     Car createCar();
4     
5 }
View Code

3. Audi.java

1 public class Audi implements Car {
2 
3     @Override
4     public void run() {
5         System.out.println("奧迪在跑!");
6     }
7 
8 }
View Code

4. AudiFactory.java

1 public class AudiFactory implements CarFactory {
2 
3     @Override
4     public Car createCar() {
5         return new Audi();
6     }
7 
8 }
View Code

5. Byd.java

1 public class Byd implements Car {
2 
3     @Override
4     public void run() {
5         System.out.println("比亞迪在跑!");
6     }
7 
8 }
View Code

6. BydFactory.java

1 public class BydFactory implements CarFactory {
2 
3     @Override
4     public Car createCar() {
5         return new Byd();
6     }
7 
8 }
View Code

7. Client.java

 1 public class Client {
 2 
 3     public static void main(String[] args) {
 4         Car car1 = new AudiFactory().createCar();
 5         Car car2 = new BydFactory().createCar();
 6         car1.run();
 7         car2.run();
 8     }
 9 
10 }
View Code

代碼(抽象工廠)

1. Engine.java

 1 public interface Engine {
 2 
 3     void start();
 4     
 5     void speedUp();
 6     
 7 }
 8 
 9 class LuxuryEngine implements Engine {
10     
11     @Override
12     public void start() {
13         System.out.println("快速啟動!");
14     }
15     
16     @Override
17     public void speedUp() {
18         System.out.println("快速加速!");
19     }
20     
21 }
22 
23 class LowEngine implements Engine {
24 
25     @Override
26     public void start() {
27         System.out.println("慢速啟動!");
28     }
29 
30     @Override
31     public void speedUp() {
32         System.out.println("慢速加速!");
33     }
34     
35 }
View Code

2. Seat.java

 1 public interface Seat {
 2 
 3     void massage();
 4     
 5 }
 6 
 7 class LuxurySeat implements Seat {
 8     
 9     @Override
10     public void massage() {
11         System.out.println("按摩!");
12     }
13     
14 }
15 
16 class LowSeat implements Seat {
17 
18     @Override
19     public void massage() {
20         System.out.println("不能按摩!");
21     }
22     
23 }
View Code

3. Tire.java

 1 public interface Tire {
 2 
 3     void revolve();
 4     
 5 }
 6 
 7 class LuxuryTire implements Tire {
 8 
 9     @Override
10     public void revolve() {
11         System.out.println("旋轉不磨損!");
12     }
13     
14 }
15 
16 class LowTire implements Tire {
17 
18     @Override
19     public void revolve() {
20         System.out.println("旋轉磨損快!");
21     }
22     
23 }
View Code

4. CarFactory.java

 1 public interface CarFactory {
 2 
 3     Engine createEngine();
 4     
 5     Seat createSeat();
 6     
 7     Tire createTire();
 8     
 9 }
10 
11 class LuxuryCarFactory implements CarFactory {
12     
13     @Override
14     public Engine createEngine() {
15         return new LuxuryEngine();
16     }
17     
18     @Override
19     public Seat createSeat() {
20         return new LuxurySeat();
21     }
22     
23     @Override
24     public Tire createTire() {
25         return new LuxuryTire();
26     }
27     
28 }
29 
30 class LowCarFactory implements CarFactory {
31 
32     @Override
33     public Engine createEngine() {
34         return new LowEngine();
35     }
36 
37     @Override
38     public Seat createSeat() {
39         return new LowSeat();
40     }
41 
42     @Override
43     public Tire createTire() {
44         return new LowTire();
45     }
46     
47 }
View Code

5. Client.java

 1 public class Client {
 2 
 3     public static void main(String[] args) {
 4         CarFactory carFactory = new LuxuryCarFactory();
 5         Engine engine = carFactory.createEngine();
 6         engine.start();
 7         engine.speedUp();
 8     }
 9 
10 }
View Code

應用場景

1. JDK中Calendar.getInstance()方法。

2. JDBC中Connection對象的獲取。

3. Hibernate中SessionFactory創建Session。

4. Spring中IoC容器創建管理Bean對象。

5. 反射中Class對象的newInstance()方法。

構建者模式(Builder)

效果

1. 構建一個復雜的產品(如神舟飛船、iPhone)時,需要解決“如何裝配子組件”的問題。

2. 分離了對象子組件的單獨構建(Builder)和裝配(Director),從而構造出復雜對象。

3. 由於實現了構建和裝配的解耦。不同的構建器、相同的裝配,或者相同的構建器、不同的裝配,都可以創建不同的對象。

4. 建造者模式一般與工廠模式搭配,由工廠模式創建子組件,再有建造者模式裝配。

代碼

1. Airship.java

 1 public class Airship {
 2 
 3     private Engine engine;
 4     
 5     private OrbitalModule orbitalModule;
 6     
 7     private EscapeTower escapeTower;
 8     
 9     public void launch() {
10         System.out.println(engine.getName() + "點火!");
11         System.out.println(orbitalModule.getName() + "發射!");
12     }
13 
14     public Engine getEngine() {
15         return engine;
16     }
17 
18     public void setEngine(Engine engine) {
19         this.engine = engine;
20     }
21 
22     public OrbitalModule getOrbitalModule() {
23         return orbitalModule;
24     }
25 
26     public void setOrbitalModule(OrbitalModule orbitalModule) {
27         this.orbitalModule = orbitalModule;
28     }
29 
30     public EscapeTower getEscapeTower() {
31         return escapeTower;
32     }
33 
34     public void setEscapeTower(EscapeTower escapeTower) {
35         this.escapeTower = escapeTower;
36     }
37     
38 }
39 
40 class Engine {
41     
42     private String name;
43     
44     public String getName() {
45         return name;
46     }
47     
48     public void setName(String name) {
49         this.name = name;
50     }
51     
52 }
53 
54 class OrbitalModule {
55     
56     private String name;
57     
58     public String getName() {
59         return name;
60     }
61     
62     public void setName(String name) {
63         this.name = name;
64     }
65     
66 }
67 
68 class EscapeTower {
69     
70     private String name;
71 
72     public String getName() {
73         return name;
74     }
75 
76     public void setName(String name) {
77         this.name = name;
78     }
79     
80 }
View Code

2. AirshipBuilder.java

1 public interface AirshipBuilder {
2 
3     Engine buildEngine();
4     
5     OrbitalModule buildOrbitalModule();
6     
7     EscapeTower buildEscapeTower();
8     
9 }
View Code

3. AirshipDirector.java

1 public interface AirshipDirector {
2 
3     Airship directAirship();
4     
5 }
View Code

4. SzAirshipBuilder.java

 1 public class SzAirshipBuilder implements AirshipBuilder {
 2 
 3     @Override
 4     public Engine buildEngine() {
 5         // 也可使用工廠模式創建
 6         Engine engine = new Engine();
 7         engine.setName("神舟發動機");
 8         return engine;
 9     }
10 
11     @Override
12     public OrbitalModule buildOrbitalModule() {
13         // 也可使用工廠模式創建
14         OrbitalModule orbitalModule = new OrbitalModule();
15         orbitalModule.setName("神舟軌道艙");
16         return orbitalModule;
17     }
18 
19     @Override
20     public EscapeTower buildEscapeTower() {
21         // 也可使用工廠模式創建
22         EscapeTower escapeTower = new EscapeTower();
23         escapeTower.setName("神舟逃逸塔");
24         return escapeTower;
25     }
26 
27 }
View Code

5. SzAirshipDirector.java

 1 public class SzAirshipDirector implements AirshipDirector {
 2 
 3     private AirshipBuilder airshipBuilder;
 4     
 5     public SzAirshipDirector(AirshipBuilder airshipBuilder) {
 6         this.airshipBuilder = airshipBuilder;
 7     }
 8 
 9     @Override
10     public Airship directAirship() {
11         Engine engine = airshipBuilder.buildEngine();
12         OrbitalModule orbitalModule = airshipBuilder.buildOrbitalModule();
13         EscapeTower escapeTower = airshipBuilder.buildEscapeTower();
14         
15         Airship airship = new Airship();
16         airship.setEngine(engine);
17         airship.setOrbitalModule(orbitalModule);
18         airship.setEscapeTower(escapeTower);
19         
20         return airship;
21     }
22 
23 }
View Code

6. Client.java

1 public class Client {
2 
3     public static void main(String[] args) {
4         AirshipDirector airshipDirector = new SzAirshipDirector(new SzAirshipBuilder());
5         Airship airship = airshipDirector.directAirship();
6         airship.launch();
7     }
8     
9 }
View Code

應用場景

1. StringBuilder.append()方法。

2. SQL中的PareparedStatement。

原型模式(Prototype)

效果

1. 通過new創建對象需要非常繁瑣的數據准備或訪問權限,則可以使用原型模式。

2. 就是Java中的克隆技術,以某個對象為原型,復制出新的對象。

3. 優勢:效率高,避免重新執行構建過程。

4. 克隆類似於new,但不同於new。new創建新的對象屬性采用默認值。克隆出的對象屬性完全與原型對象相同,並且克隆出的新對象改變不會影響原型對象,然后再修改克隆對象的值。

核心角色

1. Clonable接口的clone()方法。

2. 實現原型模式最困難的是內存復制操作,所幸Java提供了clone()方法。

代碼(淺克隆)

1. Sheep.java

 1 import java.util.Date;
 2 
 3 public class Sheep implements Cloneable {
 4 
 5     private String name;
 6     
 7     private Date birthday;
 8     
 9     @Override
10     protected Object clone() throws CloneNotSupportedException {
11         return super.clone();
12     }
13 
14     public String getName() {
15         return name;
16     }
17 
18     public void setName(String name) {
19         this.name = name;
20     }
21 
22     public Date getBirthday() {
23         return birthday;
24     }
25 
26     public void setBirthday(Date birthday) {
27         this.birthday = birthday;
28     }
29     
30 }
View Code

2. Client.java

 1 import java.util.Date;
 2 
 3 public class Client {
 4 
 5     public static void main(String[] args) throws Exception {
 6         Sheep sheep1 = new Sheep();
 7         sheep1.setName("少利");
 8         sheep1.setBirthday(new Date());
 9         
10         Sheep sheep2 = (Sheep) sheep1.clone();
11         sheep2.setName("多利");
12         System.out.println(sheep2.getName());
13         System.out.println(sheep2.getBirthday());
14         
15         // 淺克隆
16         System.out.println(sheep1.getBirthday() == sheep2.getBirthday());
17     }
18 
19 }
View Code

代碼(基於JDK的深克隆)

1. Sheep.java

 1 import java.util.Date;
 2 
 3 public class Sheep implements Cloneable {
 4 
 5     private String name;
 6     
 7     private Date birthday;
 8     
 9     @Override
10     protected Object clone() throws CloneNotSupportedException {
11         Sheep sheep = (Sheep) super.clone();
12         sheep.setBirthday((Date) birthday.clone());
13         return sheep;
14     }
15 
16     public String getName() {
17         return name;
18     }
19 
20     public void setName(String name) {
21         this.name = name;
22     }
23 
24     public Date getBirthday() {
25         return birthday;
26     }
27 
28     public void setBirthday(Date birthday) {
29         this.birthday = birthday;
30     }
31     
32 }
View Code

2. Client.java

 1 import java.util.Date;
 2 
 3 public class Client {
 4 
 5     public static void main(String[] args) throws Exception {
 6         Sheep sheep1 = new Sheep();
 7         sheep1.setName("少利");
 8         sheep1.setBirthday(new Date());
 9         
10         Sheep sheep2 = (Sheep) sheep1.clone();
11         sheep2.setName("多利");
12         System.out.println(sheep2.getName());
13         System.out.println(sheep2.getBirthday());
14         
15         // 深克隆
16         System.out.println(sheep1.getBirthday() == sheep2.getBirthday());
17     }
18 
19 }
View Code

代碼(基於序列化的深克隆)

1. Sheep.java

 1 import java.io.ByteArrayInputStream;
 2 import java.io.ByteArrayOutputStream;
 3 import java.io.IOException;
 4 import java.io.ObjectInputStream;
 5 import java.io.ObjectOutputStream;
 6 import java.io.Serializable;
 7 import java.util.Date;
 8 
 9 public class Sheep implements Cloneable, Serializable {
10 
11     private static final long serialVersionUID = 2155997264135266066L;
12 
13     private String name;
14     
15     private Date birthday;
16     
17     @Override
18     protected Object clone() throws CloneNotSupportedException {
19         Sheep sheep = null;
20         ByteArrayOutputStream baos = null;
21         ObjectOutputStream oos = null;
22         ByteArrayInputStream bais = null;
23         ObjectInputStream ois = null;
24         try {
25             baos = new ByteArrayOutputStream();
26             oos = new ObjectOutputStream(baos);
27             oos.writeObject(this);
28             byte[] bytes = baos.toByteArray();
29             bais = new ByteArrayInputStream(bytes);
30             ois = new ObjectInputStream(bais);
31             sheep = (Sheep) ois.readObject();
32             
33         } catch (IOException | ClassNotFoundException e) {
34             e.printStackTrace();
35         }
36         return sheep;
37     }
38 
39     public String getName() {
40         return name;
41     }
42 
43     public void setName(String name) {
44         this.name = name;
45     }
46 
47     public Date getBirthday() {
48         return birthday;
49     }
50 
51     public void setBirthday(Date birthday) {
52         this.birthday = birthday;
53     }
54     
55 }
View Code

2. Client.java

 1 import java.util.Date;
 2 
 3 public class Client {
 4 
 5     public static void main(String[] args) throws Exception {
 6         Sheep sheep1 = new Sheep();
 7         sheep1.setName("少利");
 8         sheep1.setBirthday(new Date());
 9         
10         Sheep sheep2 = (Sheep) sheep1.clone();
11         sheep2.setName("多利");
12         System.out.println(sheep2.getName());
13         System.out.println(sheep2.getBirthday());
14         
15         // 深克隆
16         System.out.println(sheep1.getBirthday() == sheep2.getBirthday());
17     }
18 
19 }
View Code

應用場景

1. 原型模式很少單獨使用,一般與工廠模式一起出現。通過clone()方法創建對象后由工廠模式返回。

2. Spring的Bean創建有單例模式和原型模式兩種方式。

結構型模式

適配器模式(Adapter)

效果

將一個類的接口轉換成客戶希望的另一個接口,使原本由於接口不兼容而不能一起工作的那些類可以在一起工作。

核心角色

1. 目標接口(Target):客戶所期望的接口(接口、抽象類或具體類)。

2. 適配的類(Adaptee)。

3. 適配器(Adapter):通過包裝適配的類,把原接口轉換成目標接口。

分類

1. 使用繼承實現:Adapter繼承Adaptee。

2. 使用關聯實現:Adapter持有Adaptee的引用,Adapter可以繼承其他類,更靈活。

代碼(使用繼承)

1. Target.java

1 public interface Target {
2 
3     void handleRequest();
4     
5 }
View Code

2. Adaptee.java

1 public class Adaptee {
2 
3     public void request() {
4         System.out.println("處理請求!");
5     }
6     
7 }
View Code

3. Adapter.java

1 public class Adapter extends Adaptee implements Target {
2     
3     @Override
4     public void handleRequest() {
5         request();
6     }
7 
8 }
View Code

4. Client.java

 1 public class Client {
 2     
 3     public void execute(Target target) {
 4         target.handleRequest();
 5     }
 6 
 7     public static void main(String[] args) {
 8         Target target = new Adapter();
 9         new Client().execute(target);
10     }
11 
12 }
View Code

代碼(使用關聯)

1. Target.java

1 public interface Target {
2 
3     void handleRequest();
4     
5 }
View Code

2. Adaptee.java

1 public class Adaptee {
2 
3     public void request() {
4         System.out.println("處理請求!");
5     }
6     
7 }
View Code

3. Adapter.java

 1 public class Adapter implements Target {
 2     
 3     // 使用關聯更靈活,這樣適配器可以繼承其他類
 4     private Adaptee adaptee;
 5 
 6     public Adapter(Adaptee adaptee) {
 7         this.adaptee = adaptee;
 8     }
 9 
10     @Override
11     public void handleRequest() {
12         adaptee.request();
13     }
14 
15 }
View Code

4. Client.java

 1 public class Client {
 2     
 3     public void execute(Target target) {
 4         target.handleRequest();
 5     }
 6 
 7     public static void main(String[] args) {
 8         Target target = new Adapter();
 9         new Client().execute(target);
10     }
11 
12 }
View Code

應用場景

1. 做舊系統改造和升級。

2. java.io.InputStreamReader(InputStream)。

3. java.io.OutputStreamWriter(OutputStream)。

代理模式(Proxy)

效果

1. 通過代理,控制對對象的訪問。

2. 可以詳細控制訪問某個(某類)對象的方法,在調用方法前做前置處理,調用方法后做后置處理(即APO的微觀實現)。

3. AOP(Aspect Oriented Programming,面向切面編程)的核心實現機制。

核心角色

1. 抽象角色:定義代理角色和真實角色的公共對外方法。

2. 真實角色:實現抽象角色,定義真實角色所要實現的業務邏輯,供代理角色調用。

3. 代理角色:實現抽象角色,是真實角色的代理,通過真實角色的業務邏輯方法來實現抽象方法,並可以附加自己操作。

分類

1. 靜態代理(靜態定義代理類)。

2. 動態代理(動態定義代理類):

    a) JDK自帶的動態代理(java.lang.reflect.Proxy動態生成代理類和對象,java.lang.reflect.InvocationHandler通過invoke()方法實現訪問真實角色);

    b) javaassist字節碼操作庫實現;

    c) CGLIB;

    d) ASM(底層使用指令,可維護性較差)。

代碼(靜態代理)

1. Star.java

 1 public interface Star {
 2 
 3     void confer();
 4     
 5     void signContract();
 6     
 7     void bookTicket();
 8     
 9     void sing();
10     
11     void collectMoney();
12     
13 }
View Code

2. RealStar.java

 1 public class RealStar implements Star {
 2 
 3     @Override
 4     public void confer() {
 5         System.out.println("RealStar.confer()");
 6     }
 7 
 8     @Override
 9     public void signContract() {
10         System.out.println("RealStar.signContract()");
11     }
12 
13     @Override
14     public void bookTicket() {
15         System.out.println("RealStar.bookTicket()");
16     }
17 
18     @Override
19     public void sing() {
20         System.out.println("RealStar.sing()");
21     }
22 
23     @Override
24     public void collectMoney() {
25         System.out.println("RealStar.collectMoney()");
26     }
27 
28 }
View Code

3. ProxyStar.java

 1 public class ProxyStar implements Star {
 2     
 3     private Star realStar;
 4     
 5     public ProxyStar(Star realStar) {
 6         this.realStar = realStar;
 7     }
 8 
 9     @Override
10     public void confer() {
11         System.out.println("ProxyStar.confer()");
12     }
13 
14     @Override
15     public void signContract() {
16         System.out.println("ProxyStar.signContract()");
17     }
18 
19     @Override
20     public void bookTicket() {
21         System.out.println("ProxyStar.bookTicket()");
22     }
23 
24     @Override
25     public void sing() {
26         realStar.sing();
27     }
28 
29     @Override
30     public void collectMoney() {
31         System.out.println("ProxyStar.collectMoney()");
32     }
33 
34 }
View Code

4. Client.java

 1 public class Client {
 2 
 3     public static void main(String[] args) {
 4         RealStar realStar = new RealStar();
 5         ProxyStar proxyStar = new ProxyStar(realStar);
 6         proxyStar.confer();
 7         proxyStar.signContract();
 8         proxyStar.bookTicket();
 9         proxyStar.sing();
10         proxyStar.collectMoney();
11     }
12 
13 }
View Code

代碼(動態代理)

1. Star.java

 1 public interface Star {
 2 
 3     void confer();
 4     
 5     void signContract();
 6     
 7     void bookTicket();
 8     
 9     void sing();
10     
11     void collectMoney();
12     
13 }
View Code

2. RealStar.java

 1 public class RealStar implements Star {
 2 
 3     @Override
 4     public void confer() {
 5         System.out.println("RealStar.confer()");
 6     }
 7 
 8     @Override
 9     public void signContract() {
10         System.out.println("RealStar.signContract()");
11     }
12 
13     @Override
14     public void bookTicket() {
15         System.out.println("RealStar.bookTicket()");
16     }
17 
18     @Override
19     public void sing() {
20         System.out.println("RealStar.sing()");
21     }
22 
23     @Override
24     public void collectMoney() {
25         System.out.println("RealStar.collectMoney()");
26     }
27 
28 }
View Code

3. StarHandler.java

 1 import java.lang.reflect.InvocationHandler;
 2 import java.lang.reflect.Method;
 3 
 4 public class StarHandler implements InvocationHandler {
 5     
 6     private Star realStar;
 7 
 8     public StarHandler(Star realStar) {
 9         this.realStar = realStar;
10     }
11 
12     @Override
13     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
14         switch (method.getName()) {
15         case "confer":
16             System.out.println("ProxyStar.confer()");
17             break;
18         case "signContract":
19             System.out.println("ProxyStar.signContract()");
20             break;
21         case "bookTicket":
22             System.out.println("ProxyStar.bookTicket()");
23             break;
24         case "sing":
25             method.invoke(realStar, args);
26             break;
27         case "collectMoney":
28             System.out.println("ProxyStar.collectMoney()");
29             break;
30         }
31         
32         return null;
33     }
34 
35 }
View Code

4. Client.java

 1 import java.lang.reflect.Proxy;
 2 
 3 public class Client {
 4 
 5     public static void main(String[] args) {
 6         Star realStar = new RealStar();
 7         StarHandler handler = new StarHandler(realStar);
 8         Star proxyStar = (Star) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[] { Star.class }, handler);
 9         proxyStar.confer();
10         proxyStar.signContract();
11         proxyStar.bookTicket();
12         proxyStar.sing();
13         proxyStar.collectMoney();
14     }
15 
16 }
View Code

應用場景

1. 安全代理:屏蔽對真實角色的直接訪問。

2. 遠程代理:通過代理類處理遠程方法調用(RMI)。

3. 延遲代理:先加載輕量級代理對象,真正需要時再加載真實對象。

橋接模式(Bridge)

效果

1. 處理多層繼承結構、處理多維變化的場景,將各個維度設計成獨立的繼承結構,使各個維度可以獨立的擴展在抽象層建立關聯。

2. 以商場系統中的電腦分類為例,多層繼承不利於擴展性(類個數膨脹問題):

    a) 如果要增加一個新的電腦類型智能手機,則要增加各個品牌下面的類。

    b) 如果要增加一個新的品牌,也要增加各種電腦類型的類。

3. 違反單一職責原則:比如一個聯想筆記本,有兩個引起這個類變化的原因。

4. 橋接模式將電腦拆分為類型和品牌兩個維度。

代碼

1. Computer.java

 1 public abstract class Computer {
 2 
 3     protected Brand brand;
 4 
 5     public Computer(Brand brand) {
 6         this.brand = brand;
 7     }
 8     
 9     public abstract void run();
10     
11 }
12 
13 class Desktop extends Computer {
14 
15     public Desktop(Brand brand) {
16         super(brand);
17     }
18 
19     @Override
20     public void run() {
21         System.out.println(brand.getName() + "台式機運行!");
22     }
23     
24 }
25 
26 class Laptop extends Computer {
27 
28     public Laptop(Brand brand) {
29         super(brand);
30     }
31 
32     @Override
33     public void run() {
34         System.out.println(brand.getName() + "筆記本運行!");
35     }
36     
37 }
View Code

2. Brand.java

 1 public interface Brand {
 2 
 3     String getName();
 4     
 5 }
 6 
 7 class Lenovo implements Brand {
 8     
 9     @Override
10     public String getName() {
11         return "聯想";
12     }
13     
14 }
15 
16 class Dell implements Brand {
17 
18     @Override
19     public String getName() {
20         return "戴爾";
21     }
22     
23 }
View Code

3. Client.java

1 public class Client {
2 
3     public static void main(String[] args) {
4         Brand brand = new Lenovo();
5         Computer computer = new Laptop(brand);
6         computer.run();
7     }
8 
9 }
View Code

應用場景

1. JDBC驅動程序。

2. 銀行日志管理。

    a) 格式分類:操作日志、交易日志、異常日志。

    b) 距離分類:本地記錄日志、異地記錄日志。

3. 人力資源系統的獎金計算模塊。

    a) 獎金分類:個人獎金、團體獎金、激勵獎金。

    b) 部門分類:人事部門、銷售部門、研發部門。

4. OA系統的消息處理。

    a) 業務類型:普通消息、加急消息、特急消息。

    b) 發送消息方式:系統內消息、手機短信、郵件。

組合模式(Composite)

效果

1. 把部分和整理的關系用樹形結構表示,從而使客戶端可以使用統一方式處理部分對象和整體對象。

2. 組合模式為處理樹形結構提供了完美的解決方案,描述了如何將容器和葉子進行遞歸組合,使得用戶在使用時可以一致性的對待容器和葉子。

核心角色

1. 抽象構件角色(Component):定義了葉子和容器構件的共同點。

2. 葉子構件角色(Leaf):無子節點。

3. 容器構件角色(Composite):有容器特征,可以包含子節點。

代碼

Component.java

 1 import java.util.List;
 2 
 3 public interface Component {
 4 
 5     void operation();
 6     
 7 }
 8 
 9 interface Leaf extends Component {
10     
11 }
12 
13 interface Composite extends Component {
14     
15     void add(Component child);
16     
17     void remove(Component child);
18     
19     List<Component> getChildren();
20     
21 }
View Code

代碼(殺毒舉例)

1. AbstractFile.java

 1 import java.util.ArrayList;
 2 import java.util.List;
 3 
 4 public interface AbstractFile {
 5 
 6     void killVirus();
 7     
 8 }
 9 
10 class File implements AbstractFile {
11     
12     private String name;
13 
14     public File(String name) {
15         this.name = name;
16     }
17 
18     @Override
19     public void killVirus() {
20         System.out.println(name + " 文件殺毒!");
21     }
22     
23 }
24 
25 class Folder implements AbstractFile {
26     
27     private String name;
28     private List<AbstractFile> children = new ArrayList<>();
29 
30     public Folder(String name) {
31         this.name = name;
32     }
33     
34     public void add(AbstractFile child) {
35         children.add(child);
36     }
37     
38     public void remove(AbstractFile child) {
39         children.remove(child);
40     }
41 
42     @Override
43     public void killVirus() {
44         for (AbstractFile child : children) {
45             child.killVirus();
46         }
47         System.out.println(name + " 文件夾殺毒!");
48     }
49     
50 }
View Code

2. Client.java

 1 public class Client {
 2 
 3     public static void main(String[] args) {
 4         Folder myDocument = new Folder("我的文檔");
 5         myDocument.add(new File("Client.java"));
 6         myDocument.add(new File("index.php"));
 7         myDocument.add(new File("老師.avi"));
 8         Folder book = new Folder("圖書");
 9         book.add(new File("設計模式.pdf"));
10         book.add(new File("Hadoop權威指南.pdf"));
11         myDocument.add(book);
12         myDocument.killVirus();
13     }
14 
15 }
View Code

應用場景

1. 操作系統的資源管理器。

2. GUI的容器層次圖。

3. XML文件解析。

4. OA系統的組織結構處理。

5. JUnit單元測試框架:Test接口(抽象)、TestCase(葉子)、TestUnit(容器)。

裝飾器模式(Decorator)

效果

1. 又稱包裝器模式(Wrapper)。

2. 動態的為一個對象增加新的功能。

3. 裝飾器模式是一種用於代替繼承的技術,無須通過繼承增加子類就能擴展對象的新功能。使用對象的關聯關系代替繼承關系,更加靈活,同時避免類型體系的快速膨脹。

4. 裝飾器模式降低系統的耦合度,可以動態的增加或刪除對象的職責,並使得需要裝飾的具體構件類和具體裝飾類可以獨立變化,以便增加新的具體構件類和具體裝飾類。

核心角色

1. 抽象構件角色(Component):具體構件角色和裝飾角色有相同的接口,客戶端能夠以與具體構件角色相同的方式同裝飾角色交互。

2. 具體構件角色(ConcreteComponent)。

3. 裝飾角色(Decorator):持有一個抽象構件角色引用,接收所有客戶端請求,並把這些請求轉發給具體裝飾角色。

4. 具體裝飾角色(ConcreteDecorator):給構件增加新功能。

代碼

1. ICar.java

 1 public interface ICar {
 2 
 3     void move();
 4     
 5 }
 6 
 7 // 具體構建角色
 8 class Car implements ICar {
 9 
10     @Override
11     public void move() {
12         System.out.println("陸地上跑!");
13     }
14     
15 }
16 
17 // 裝飾器角色
18 abstract class SuperCar implements ICar {
19 
20     private ICar car;
21     
22     public SuperCar(ICar car) {
23         this.car = car;
24     }
25 
26     public abstract void move();
27     
28 }
29 
30 class FlyCar extends SuperCar {
31 
32     public FlyCar(ICar car) {
33         super(car);
34     }
35     
36     @Override
37     public void move() {
38         super.move();
39         fly();
40     }
41     
42     public void fly() {
43         System.out.println("天上飛!");
44     }
45     
46 }
47 
48 class WaterCar extends SuperCar {
49     
50     public WaterCar(ICar car) {
51         super(car);
52     }
53 
54     @Override
55     public void move() {
56         super.move();
57         swim();
58     }
59     
60     public void swim() {
61         System.out.println("水上游!");
62     }
63     
64 }
65 
66 class AICar extends SuperCar {
67 
68     public AICar(ICar car) {
69         super(car);
70     }
71 
72     @Override
73     public void move() {
74         super.move();
75         autoMove();
76     }
77     
78     public void autoMove() {
79         System.out.println("自動跑!");
80     }
81     
82 }
View Code

2. Client.java

 1 public class Client {
 2 
 3     public static void main(String[] args) {
 4         Car car = new Car();
 5         car.move();
 6         
 7         // 陸地上跑 + 天上飛
 8         FlyCar flyCar = new FlyCar(car);
 9         flyCar.move();
10         
11         // 陸地上跑  + 水上游
12         WaterCar waterCar = new WaterCar(car);
13         waterCar.move();
14         
15         // 陸地上跑  + 自動跑
16         AICar aiCar = new AICar(car);
17         aiCar.move();
18         
19         // 陸地上跑 + 天上飛 + 水上游
20         WaterCar flyWaterCar = new WaterCar(flyCar);
21         flyWaterCar.move();
22         
23         // 陸地上跑 + 天上飛 + 水上游 + 自動跑
24         AICar aiFlyWaterCar = new AICar(flyWaterCar);
25         aiFlyWaterCar.move();
26     }
27 
28 }
View Code

應用場景

1. IO的InputStream、OutputStream、Reader、Writer的設計。

2. Servlet API的request對象的默認實現類HttpServletRequestWrapper。

外觀模式(Facade)

效果

1. 為子系統提供統一的入口。封裝子系統的復雜性,便於客戶端調用。

2. 迪米特法則:一個軟件實體應當盡可能少地與其他實體發生相互作用。

代碼

1. Tea.java

1 public interface Tea {
2 
3     String getName();
4     
5     void drink();
6     
7 }
View Code

2. WaitressFacade.java

  1 public class WaitressFacade {
  2 
  3     public Tea makeTea(String teaName) {
  4         Water water = new Water("農夫山泉", 100.0);
  5         Tea tea = null;
  6         switch (teaName) {
  7         case "西湖龍井":
  8             tea = new XiHuLongJing();
  9             break;
 10         case "碧螺春":
 11             tea = new BiLuoChun();
 12             break;
 13         case "鐵觀音":
 14             tea = new TieGuanYin();
 15             break;
 16         default:
 17             return null;
 18         }
 19         TeaSet teaSet = new TeaSet();
 20         teaSet.cleanTeaSet(water);
 21         teaSet.addTea(tea);
 22         teaSet.cleanTea(water);
 23         teaSet.makeTea(water);
 24         return tea;
 25     }
 26     
 27 }
 28 
 29 class Water {
 30     
 31     private String name;
 32     
 33     private Double temperature;
 34 
 35     public Water(String name, Double temperature) {
 36         this.name = name;
 37         this.temperature = temperature;
 38     }
 39 
 40     public String getName() {
 41         return name;
 42     }
 43 
 44     public Double getTemperature() {
 45         return temperature;
 46     }
 47 
 48 }
 49 
 50 class TeaSet {
 51     
 52     private Tea tea;
 53     
 54     public void cleanTeaSet(Water water) {
 55         System.out.println("使用" + water.getTemperature() + "°的" + water.getName() + "燙洗茶具!");
 56     }
 57     
 58     public void addTea(Tea tea) {
 59         System.out.println("投入" + tea.getName() + "!");
 60         this.tea = tea;
 61     }
 62     
 63     public void cleanTea(Water water) {
 64         System.out.println("使用" + water.getTemperature() + "°的" + water.getName() + "洗茶!");
 65     }
 66     
 67     public Tea makeTea(Water water) {
 68         System.out.println("使用" + water.getTemperature() + "°的" + water.getName() + "泡茶!");
 69         return tea;
 70     }
 71     
 72 }
 73 
 74 class XiHuLongJing implements Tea {
 75     
 76     @Override
 77     public String getName() {
 78         return "西湖龍井";
 79     }
 80 
 81     @Override
 82     public void drink() {
 83         System.out.println("品" + getName() + "!");
 84     }
 85     
 86 }
 87 
 88 class BiLuoChun implements Tea {
 89     
 90     @Override
 91     public String getName() {
 92         return "洞庭碧螺春";
 93     }
 94     
 95     @Override
 96     public void drink() {
 97         System.out.println("品" + getName() + "!");
 98     }
 99     
100 }
101 
102 class TieGuanYin implements Tea {
103 
104     @Override
105     public String getName() {
106         return "安溪鐵觀音";
107     }
108     
109     @Override
110     public void drink() {
111         System.out.println("品" + getName() + "!");
112     }
113     
114 }
View Code

3. Client.java

1 public class Client {
2 
3     public static void main(String[] args) {
4         WaitressFacade waitressFacade = new WaitressFacade();
5         Tea tea = waitressFacade.makeTea("鐵觀音");
6         tea.drink();
7     }
8 
9 }
View Code

應用場景

1. 頻率很高,處處用到。

2. JDBC封裝后的DbUtils類。

3. Hibernate提供的工具類。

4. Spring JDBC工具類等。

享元模式(FlyWeight)

效果

1. 享元模式以共享的方式高效地支持大量細粒度對象的重用。

    a) 內存屬於稀缺資源,不要隨便浪費。如果有很多個完全相同或相似的對象,我們可以通過享元模式節省內存。

    b) 用時間換取空間。

2. 享元對象能做到共享的關鍵是區分了內部狀態和外部狀態。

    a) 內部狀態:可以共享,不會隨環境變化而變化。

    b) 外部狀態:不可以共享,會隨環境變化而變化。

核心角色

1. 享元工廠類(FlyWeightFactory):創建並管理享元對象,享元池一般設計成鍵值對。

2. 抽象享元類(FlyWeight):通常是一個接口或抽象類,聲明公共方法向外界提供對象的內部狀態、設置外部狀態。

3. 具體享元類(ConcreteFlyWeight):為內部狀態提供成員變量進行存儲。

4. 非共享享元類(UnsharedConcreteFlyWeight):不能被共享的子類可以設計為非共享享元類。

代碼

1. ChessFlyWeight.java

1 // 抽象享元類
2 public interface ChessFlyWeight {
3 
4     String getColor();
5     
6     void display(Position position);
7     
8 }
View Code

2. Position.java

 1 // 非共享享元類
 2 public class Position {
 3 
 4     private int x;
 5     
 6     private int y;
 7 
 8     public Position(int x, int y) {
 9         this.x = x;
10         this.y = y;
11     }
12 
13     public int getX() {
14         return x;
15     }
16 
17     public int getY() {
18         return y;
19     }
20 
21 }
View Code

3. ConcreteChess.java

 1 // 具體享元類
 2 public class ConcreteChess implements ChessFlyWeight {
 3     
 4     private String color;
 5 
 6     public ConcreteChess(String color) {
 7         this.color = color;
 8     }
 9     
10     @Override
11     public String getColor() {
12         return color;
13     }
14 
15     @Override
16     public void display(Position position) {
17         System.out.println("顯示:顏色" + color + ",位置(" + position.getX() + "," + position.getY() + ")!");
18     }
19 
20 }
View Code

4. ChessFactory.java

 1 import java.util.HashMap;
 2 import java.util.Map;
 3 
 4 // 享元工廠類
 5 public class ChessFactory {
 6 
 7     private static Map<String, ChessFlyWeight> chesses = new HashMap<>();
 8     
 9     public static ChessFlyWeight getChess(String color) {
10         ChessFlyWeight chess = chesses.get(color);
11         if (chess != null) {
12             return chess;
13         }
14         chess = new ConcreteChess(color);
15         chesses.put(color, chess);
16         return chess;
17     }
18     
19 }
View Code

5. Client.java

 1 public class Client {
 2 
 3     public static void main(String[] args) {
 4         ChessFlyWeight chess1 = ChessFactory.getChess("黑");
 5         ChessFlyWeight chess2 = ChessFactory.getChess("黑");
 6         System.out.println(chess1 == chess2);
 7         
 8         chess1.display(new Position(10, 20));
 9         chess1.display(new Position(20, 10));
10     }
11 
12 }
View Code

應用場景

1. 享元模式由於共享的特性,可以應用與任何“池”,比如線程池、數據庫連接池。

2. String類的設計。

行為型模式

責任鏈模式(Chain of Resposibility)

效果

1. 將能夠處理同一類請求的對象連成一條鏈,所提交的請求沿着鏈傳遞,鏈上的對象逐個判斷是否有能力處理該請求,如果能則處理,否則傳遞給鏈上的下一個對象。

2. 請假條審批過程:天數小於3天,主任審批;大於等於3天,小於10天,經理審批;大於等於10天,小於30天,總經理審批;大於等於30天,拒絕。

核心角色

1. 責任鏈既可以通過LinkedList實現,也可以通過ArrayList實現。

2. 可事先定義好責任鏈存儲到配置文件或數據庫。

代碼

1. LeaveRequest.java

 1 public class LeaveRequest {
 2 
 3     private String name;
 4     
 5     private int leaveDays;
 6     
 7     private String reason;
 8 
 9     public LeaveRequest(String name, int leaveDays, String reason) {
10         this.name = name;
11         this.leaveDays = leaveDays;
12         this.reason = reason;
13     }
14 
15     public String getName() {
16         return name;
17     }
18 
19     public int getLeaveDays() {
20         return leaveDays;
21     }
22 
23     public String getReason() {
24         return reason;
25     }
26 
27 }
View Code

2. Leave.java

 1 public abstract class Leader {
 2 
 3     protected String name;
 4     
 5     protected Leader nextLeader;
 6 
 7     public Leader(String name) {
 8         this.name = name;
 9     }
10 
11     public void setNextLeader(Leader nextLeader) {
12         this.nextLeader = nextLeader;
13     }
14     
15     public abstract void handleRequest(LeaveRequest leaveRequest);
16     
17 }
18 
19 class Director extends Leader {
20 
21     public Director(String name) {
22         super(name);
23     }
24 
25     @Override
26     public void handleRequest(LeaveRequest leaveRequest) {
27         if (leaveRequest.getLeaveDays() < 3) {
28             System.out.println("主任" + name + "審批通過!");
29         } else if (nextLeader != null) {
30             nextLeader.handleRequest(leaveRequest);
31         }
32     }
33     
34 }
35 
36 class Manager extends Leader {
37     
38     public Manager(String name) {
39         super(name);
40     }
41     
42     @Override
43     public void handleRequest(LeaveRequest leaveRequest) {
44         if (leaveRequest.getLeaveDays() < 10) {
45             System.out.println("經理" + name + "審批通過!");
46         } else if (nextLeader != null) {
47             nextLeader.handleRequest(leaveRequest);
48         }
49     }
50     
51 }
52 
53 class GeneralManager extends Leader {
54 
55     public GeneralManager(String name) {
56         super(name);
57     }
58 
59     @Override
60     public void handleRequest(LeaveRequest leaveRequest) {
61         if (leaveRequest.getLeaveDays() < 30) {
62             System.out.println("總經理" + name + "審批通過!");
63         } else {
64             System.out.println("不通過!");
65         }
66     }
67     
68 }
View Code

3. Client.java

 1 public class Client {
 2 
 3     public static void main(String[] args) {
 4         Leader director = new Director("張三");
 5         Leader manager = new Manager("李四");
 6         Leader generalManager = new GeneralManager("王五");
 7         director.setNextLeader(manager);
 8         manager.setNextLeader(generalManager);
 9         
10         LeaveRequest leaveRequest = new LeaveRequest("Tom", 5, "回英國老家。");
11         director.handleRequest(leaveRequest);
12     }
13 
14 }
View Code

應用場景

1. Java的異常捕獲,try可對應多個catch,當第一個catch不匹配,則自動跳到第二個catch。

2. JavaScript的事件冒泡和捕獲機制。

3. Servlet開發中,過濾器的鏈式處理。

迭代器模式(Iterator)

效果

1. 又稱游標模式(Cursor)。

2. 提供一種可以遍歷聚合對象的方式。

核心角色

1. 聚合對象(Aggregate):存儲數據。

2. 迭代器(Iterator):遍歷數據的算法,比如正序、倒序、隨機等。

代碼

1. Iterator.java

1 public interface Iterator {
2 
3     boolean hasNext();
4     
5     Object next();
6     
7 }
View Code

2. Aggregate.java

1 public interface Aggregate {
2 
3     void add(Object element);
4     
5     void remove(Object element);
6     
7     Iterator iterator();
8     
9 }
View Code

3. ConcreteAggregate.java

 1 import java.util.ArrayList;
 2 import java.util.List;
 3 
 4 public class ConcreteAggregate implements Aggregate {
 5 
 6     private List<Object> list = new ArrayList<>();
 7     
 8     @Override
 9     public void add(Object element) {
10         list.add(element);
11     }
12 
13     @Override
14     public void remove(Object element) {
15         list.remove(element);
16     }
17 
18     @Override
19     public Iterator iterator() {
20         return new ConcreteAggregateIterator();
21     }
22     
23     // 定義成內部類,可直接訪問外部類的屬性
24     private class ConcreteAggregateIterator implements Iterator {
25 
26         private int cursor = -1;
27         
28         @Override
29         public boolean hasNext() {
30             return cursor < list.size() - 1;
31         }
32 
33         @Override
34         public Object next() {
35             cursor++;
36             return list.get(cursor);
37         }
38 
39     }
40 
41 }
View Code

4. Client.java

 1 public class Client {
 2 
 3     public static void main(String[] args) {
 4         Aggregate aggregate = new ConcreteAggregate();
 5         aggregate.add("A");
 6         aggregate.add("B");
 7         aggregate.add("C");
 8         aggregate.add("D");
 9         aggregate.add("E");
10         
11         Iterator iterator = aggregate.iterator();
12         while (iterator.hasNext()) {
13             System.out.println(iterator.next());
14         }
15     }
16 
17 }
View Code

應用場景

JDK內置的迭代器。

中介者模式(Mediator)

效果

1. 如果一個系統中對象之間的聯系呈現為網狀結構,對象之間存在大量多對多關系,將導致關系及其復雜,這些對象稱為“同事對象”。

2. 引入一個中介者對象,使各同事對象只跟中介者對象打交道,將復雜的網狀結構解耦成星狀結構。

3. 解耦多個同事對象之間的交互關系。每個對象都持有中介者對象的引用,只跟中介者對象打交道。我們通過中介者對象統一管理這些交互關系。

代碼

1. 公司有總經理,各個部門有事情都通報給總經理,總經理再通知各個相關部門。總經理起到中介、協調作用。

2. Colleague.java

1 public interface Colleague {
2 
3     void selfAction();
4     
5     void outAction();
6     
7 }
View Code

3. Mediator.java

1 public interface Mediator {
2 
3     void register(String name, Colleague colleague);
4     
5     void command(String name);
6     
7 }
View Code

4. DevelopmentDepartment.java

 1 public class DevelopmentDepartment implements Colleague {
 2 
 3     private Mediator mediator;
 4     
 5     public DevelopmentDepartment(Mediator mediator) {
 6         this.mediator = mediator;
 7         mediator.register("研發部", this);
 8     }
 9 
10     @Override
11     public void selfAction() {
12         System.out.println("專心研發!");
13     }
14 
15     @Override
16     public void outAction() {
17         System.out.println("研發部向總經理匯報:需要資金支持!");
18         mediator.command("財務部");
19     }
20 
21 }
View Code

5. MarcketDepartment.java

 1 public class MarcketDepartment implements Colleague {
 2 
 3     private Mediator mediator;
 4     
 5     public MarcketDepartment(Mediator mediator) {
 6         this.mediator = mediator;
 7         mediator.register("市場部", this);
 8     }
 9 
10     @Override
11     public void selfAction() {
12         System.out.println("專心接項目");
13     }
14 
15     @Override
16     public void outAction() {
17         System.out.println("市場部向總經理匯報:需要資金支持!");
18     }
19 
20 }
View Code

6. FinacialDepartment.java

 1 public class FinacialDepartment implements Colleague {
 2 
 3     private Mediator mediator;
 4     
 5     public FinacialDepartment(Mediator mediator) {
 6         this.mediator = mediator;
 7         mediator.register("財務部", this);
 8     }
 9 
10     @Override
11     public void selfAction() {
12         System.out.println("專心數錢!");
13     }
14 
15     @Override
16     public void outAction() {
17         System.out.println("財務部向總經理匯報:錢太多,花不完!");
18         mediator.command("市場部");
19     }
20 
21 }
View Code

7. GeneralManager.java

 1 import java.util.HashMap;
 2 import java.util.Map;
 3 
 4 public class GeneralManager implements Mediator {
 5 
 6     private Map<String, Colleague> colleagues = new HashMap<>();
 7     
 8     @Override
 9     public void register(String name, Colleague colleague) {
10         colleagues.put(name, colleague);
11     }
12 
13     @Override
14     public void command(String name) {
15         colleagues.get(name).outAction();
16     }
17 
18 }
View Code

8. Client.java

 1 public class Client {
 2 
 3     public static void main(String[] args) {
 4         Mediator generalManager = new GeneralManager();
 5         Colleague developmentDepartment = new DevelopmentDepartment(generalManager);
 6         Colleague marcketDepartment = new MarcketDepartment(generalManager);
 7         Colleague finacialDepartment = new FinacialDepartment(generalManager);
 8         
 9         developmentDepartment.selfAction();
10         developmentDepartment.outAction();
11     }
12 
13 }
View Code

應用場景

1. MVC模式的C是中介者對象,M和V都和他打交道。

2. GUI中,多個組件之間交互,可以引入一個中介者對象(整體窗口對象或DOM對象)。

3. java.lang.reflect.Method#invoke()。

命令模式(Command)

效果

1. 又稱“動作模式(Action)、事務模式(Transaction)。

2. 將一個請求封裝成一個對象,從而使我們可用不同的請求對客戶進行參數化;對請求排隊或者記錄請求日志,以及支持可撤銷的操作。

核心角色

1. 抽象命令類(Command)。

2. 具體命令類(ConcreteCommand)。

3. 調用者/請求者(Invoker):請求的發送者,通過命令對象來執行請求。調用者並不需要在設計時確定接收者,而是在運行時,調用命令對象的execute(),間接調用接

收者的相關操作。

4. 接收者(Receiver):執行與請求相關的操作,具體實現對請求的業務處理。

代碼

1. Command.java

1 public interface Command {
2 
3     // 實際中可設計多個方法
4     void execute();
5     
6 }
View Code

2. ConcreteCommand.java

 1 public class ConcreteCommand implements Command {
 2 
 3     private Receiver receiver;
 4     
 5     public ConcreteCommand(Receiver receiver) {
 6         this.receiver = receiver;
 7     }
 8 
 9     @Override
10     public void execute() {
11         // 可在執行前后做其他操作,比如記錄日志
12         receiver.action();
13     }
14     
15 }
View Code

3. Invoker.java

 1 public class Invoker {
 2 
 3     // 也可以是多條命令,類似數據庫事務中的多條命令
 4     private Command command;
 5 
 6     public Invoker(Command command) {
 7         this.command = command;
 8     }
 9     
10     public void call() {
11         command.execute();
12     }
13     
14 }
View Code

4. Receiver.java

1 public class Receiver {
2 
3     public void action() {
4         System.out.println("Receiver.action()");
5     }
6     
7 }
View Code

5. Client.java

1 public class Client {
2 
3     public static void main(String[] args) {
4         Command command = new ConcreteCommand(new Receiver());
5         Invoker invoker = new Invoker(command);
6         invoker.call();
7     }
8 
9 }
View Code

應用場景

數據庫事務機制的底層實現。

解釋器模式(Interpreter)

效果

1. 不常用的設計模式。

2. 用於描述如何構成一個簡單的語言解釋器,主要用於使用面向對象語言開發的編譯器和解釋器設計。

3. 盡量不要使用解釋器模式,后期維護非常麻煩。在項目中,可以使用JRuby、Groovy、Java的JavaScript引擎來代替解釋器,彌補Java語言不足。

應用場景

1. EL表達式的處理。

2. 正則表達式解釋器。

3. SQL語法解釋器。

4. 數學表達式解析器,如工具包Math Expression String Parser、Expression4J。

訪問者模式(Visitor)

效果

1. 不常用的設計模式。

2. 對於存儲在一個集合中的對象,他們可能具有不同的類型(即使有一個公共的接口),對於該集合中的對象,可以接收一類稱為訪問者的對象來訪問,不同的訪問者其訪問方式也有所不同。

3. 表示一個作用於某對象結構中各元素的操作,使我們可以不在改變元素類的前提下定義作用於這些元素的新操作。

應用場景

1. XML文檔解析器設計。

2. 編譯器的設計。

3. 復雜集合對象的處理。

策略模式(Strategy)

效果

1. 策略模式對應於解決某一個問題的一個算法族,允許客戶端從該算法族中任選一個算法解決問題,同時可以方便更換算法或添加新的算法。

2. 本質:分離算法,選擇實現。

代碼

1. 某個市場人員接到單后的報銷策略(CRM系統的常見問題)。報價策略很復雜:

    a) 普通客戶小批量報價;

    b) 普通客戶大批量報價;

    c) 老客戶小批量報價;

    d) 老客戶大批量報價。

2. Strategy.java

1 public interface Strategy {
2 
3     double getPrice(double standardPrice);
4     
5 }
View Code

3. Context.java

 1 // 負責與具體的策略類交互,將客戶端與算法分離
 2 public class Context {
 3 
 4     private Strategy strategy;
 5 
 6     public Context(Strategy strategy) {
 7         this.strategy = strategy;
 8     }
 9     
10     public void printPrice(double standardPrice) {
11         System.out.println(strategy.getPrice(standardPrice));
12     }
13     
14 }
View Code

4. NewCustomerFewStrategy.java

1 public class NewCustomerFewStrategy implements Strategy {
2 
3     @Override
4     public double getPrice(double standardPrice) {
5         System.out.println("不打折!");
6         return standardPrice;
7     }
8 
9 }
View Code

5. NewCustomerManyStrategy.java

1 public class NewCustomerManyStrategy implements Strategy {
2 
3     @Override
4     public double getPrice(double standardPrice) {
5         System.out.println("打九折!");
6         return standardPrice * 0.9;
7     }
8 
9 }
View Code

6. OldCustomerFewStrategy.java

1 public class OldCustomerFewStrategy implements Strategy {
2 
3     @Override
4     public double getPrice(double standardPrice) {
5         System.out.println("打八五折!");
6         return standardPrice * 0.85;
7     }
8 
9 }
View Code

7. OldCustomerManyStrategy.java

1 public class OldCustomerManyStrategy implements Strategy {
2 
3     @Override
4     public double getPrice(double standardPrice) {
5         System.out.println("打八折!");
6         return standardPrice * 0.8;
7     }
8 
9 }
View Code

8. Client.java

1 public class Client {
2 
3     public static void main(String[] args) {
4         Strategy strategy = new OldCustomerManyStrategy();    // 可通過配置生成
5         Context context = new Context(strategy);
6         context.printPrice(998);
7     }
8 
9 }
View Code

應用場景

1. Java的GUI編程,布局管理。

2. Spring框架的Resource接口,資源訪問策略。

3. javax.servlet.http.HttpServlet#service()。

模板方法模式(Template Method)

效果

1. 常用的模式。

2. 模板方法定義一個操作的算法框架,將某些步驟延遲到子類中實現。這樣,新的子類可以在不改變算法結構的前提下,重新定義該算法的某些特定步驟。

代碼

1. BankTemplateMethod.java

 1 public abstract class BankTemplateMethod {
 2     
 3     protected void takeNumber() {
 4         System.out.println("取號!");
 5     }
 6 
 7     protected void waitInLine() {
 8         System.out.println("排隊!");
 9     }
10     
11     // 鈎子方法/回調方法:辦理具體業務
12     protected abstract void transaction();
13     
14     protected void evaluate() {
15         System.out.println("評分!");
16     }
17     
18     // 模板方法
19     public final void process() {
20         takeNumber();
21         waitInLine();
22         transaction();
23         evaluate();
24     }
25     
26 }
View Code

2. DrawMoney.java

1 public class DrawMoney extends BankTemplateMethod {
2 
3     @Override
4     protected void transaction() {
5         System.out.println("取款!");
6     }
7 
8 }
View Code

3. Client.java

 1 public class Client {
 2 
 3     public static void main(String[] args) {
 4         BankTemplateMethod drawMoney = new DrawMoney();
 5         drawMoney.process();
 6         
 7         // 匿名內部類實現
 8         new BankTemplateMethod() {
 9 
10             @Override
11             protected void transaction() {
12                 System.out.println("存款!");
13             }
14             
15         }.process();;
16     }
17 
18 }
View Code

應用場景

1. 各框架、類庫都有模板方法。

2. 數據庫訪問的封裝。

3. JUnit單元測試。

4. Servlet的doGet()和doPost()方法調用。

5. Spring的JDBCTemplate、HibernateTemplate。

狀態模式(State)

效果

1. 用於解決系統中復雜對象的狀態轉換以及不同狀態下行為的封裝問題。

2. 酒店系統中,房間的狀態變化。

核心角色

1. 上下文類(Context):環境類中維護一個State對象,定義了當前的狀態。

2. 抽象狀態類(State)。

3. 具體狀態類(ConcreteState):每一個類封裝了一個狀態對應的行為。

代碼

1. State.java

1 public interface State {
2 
3     void handle();
4     
5 }
View Code

2. RoomContext.java

 1 public class RoomContext {
 2 
 3     private State state;
 4 
 5     public State getState() {
 6         return state;
 7     }
 8 
 9     public void setState(State state) {
10         this.state = state;
11         state.handle();
12     }
13     
14 }
View Code

3. FreeState.java

1 public class FreeState implements State {
2 
3     @Override
4     public void handle() {
5         System.out.println("退出房間!");
6     }
7 
8 }
View Code

4. BookedState.java

1 public class BookedState implements State {
2 
3     @Override
4     public void handle() {
5         System.out.println("預定房間!");
6     }
7 
8 }
View Code

5. CheckedInState.java

1 public class CheckedInState implements State {
2 
3     @Override
4     public void handle() {
5         System.out.println("入住房間!");
6     }
7 
8 }
View Code

6. Client.java

 1 public class Client {
 2 
 3     public static void main(String[] args) {
 4         RoomContext context = new RoomContext();
 5         context.setState(new FreeState());
 6         context.setState(new BookedState());
 7         context.setState(new CheckedInState());
 8     }
 9 
10 }
View Code

應用場景

1. 銀行系統的賬號狀態管理。

2. OA系統的公文狀態管理。

3. 酒店系統的房間狀態管理。

4. 線程對象各狀態之間的切換。

觀察者模式(Observer)

效果

1. 觀察者模式用於1:N的消息通知。

2. 當目標對象(Subject或Observable)的狀態變化(消息發布)時,他及時告知一系列觀察者對象(Observer),令他們做出相應(消息訂閱)。

3. 通知觀察者的方式:

    a) 推:每次都會把消息以廣播方式發送給所有觀察者,所有觀察者只能被動接收。

    b) 拉:觀察者只要知道有變化即可,什么時候獲取消息、獲取什么內容,都由觀察者自主決定。

代碼

1. Subject.java

 1 import java.util.ArrayList;
 2 import java.util.List;
 3 
 4 public abstract class Subject {
 5 
 6     private List<Observer> observers = new ArrayList<>();
 7     
 8     public void subscribe(Observer observer) {
 9         observers.add(observer);
10     }
11     
12     public void unsubscribe(Observer observer) {
13         observers.remove(observer);
14     }
15     
16     public void notifyAllObservers() {
17         for (Observer observer : observers) {
18             observer.update(this);
19         }
20     }
21     
22 }
View Code

2. ConcreteSubject.java

 1 public class ConcreteSubject extends Subject {
 2 
 3     private int state;
 4 
 5     public int getState() {
 6         return state;
 7     }
 8 
 9     public void setState(int state) {
10         this.state = state;
11         notifyAllObservers();
12     }
13     
14 }
View Code

3. Observer.java

1 public interface Observer {
2 
3     void update(Subject subject);
4     
5 }
View Code

4. ConcreteObserver.java

 1 public class ConcreteObserver implements Observer {
 2     
 3     private String name;
 4 
 5     public ConcreteObserver(String name) {
 6         this.name = name;
 7     }
 8 
 9     @Override
10     public void update(Subject subject) {
11         System.out.println(name + "收到消息:state=" + ((ConcreteSubject) subject).getState());
12     }
13 
14 }
View Code

5. Client.java

 1 public class Client {
 2 
 3     public static void main(String[] args) {
 4         ConcreteSubject subject = new ConcreteSubject();
 5         Observer observer1 = new ConcreteObserver("張三");
 6         Observer observer2 = new ConcreteObserver("李四");
 7         Observer observer3 = new ConcreteObserver("王五");
 8         subject.subscribe(observer1);
 9         subject.subscribe(observer2);
10         subject.subscribe(observer3);
11         
12         subject.setState(1);
13         subject.setState(2);
14     }
15 
16 }
View Code

代碼(基於JDK)

1. ConcreteSubject.java

 1 import java.util.Observable;
 2 
 3 public class ConcreteSubject extends Observable {
 4 
 5     private int state;
 6 
 7     public int getState() {
 8         return state;
 9     }
10 
11     public void setState(int state) {
12         this.state = state;
13         // 目標對象已變化
14         setChanged();
15         // 通知觀察者
16         notifyObservers(state);
17     }
18     
19 }
View Code

2. ConcreteObserver.java

 1 import java.util.Observable;
 2 import java.util.Observer;
 3 
 4 public class ConcreteObserver implements Observer {
 5 
 6     private String name;
 7 
 8     public ConcreteObserver(String name) {
 9         this.name = name;
10     }
11     
12     @Override
13     public void update(Observable observable, Object arg) {
14         ConcreteSubject subject = (ConcreteSubject) observable;
15         System.out.println(name + "收到消息:" + arg);
16         System.out.println(name + "獲取最新狀態:" + subject.getState());
17     }
18 
19 }
View Code

3. Client.java

 1 import java.util.Observer;
 2 
 3 public class Client {
 4 
 5     public static void main(String[] args) {
 6         ConcreteSubject subject = new ConcreteSubject();
 7         Observer observer1 = new ConcreteObserver("張三");
 8         Observer observer2 = new ConcreteObserver("李四");
 9         Observer observer3 = new ConcreteObserver("王五");
10         subject.addObserver(observer1);
11         subject.addObserver(observer2);
12         subject.addObserver(observer3);
13         
14         subject.setState(1);
15         subject.setState(2);
16     }
17 
18 }
View Code

應用場景

1. 聊天室,服務器轉發給所有客戶端。

2. 網絡游戲多人聯機對戰,服務器將客戶端的狀態進行分發。

3. 郵件訂閱。

4. Servlet編程,監聽器的實現。

5. Android,廣播機制。

6. 京東商城,群發某商品打折信息。

備忘錄模式(Memento)

效果

保存某個對象內部狀態的拷貝,以后可以將該對象恢復到原先狀態。

核心角色

1. 源發器類(Originator):負責創建一個備忘錄類,用以記錄當前內部狀態,並可使用備忘錄恢復內部狀態。

2. 備忘錄類(Memento):負責存儲源發器類的內部狀態,並可防止源發器類以外的其他對象訪問備忘錄類。

3. 負責人類(CareTaker):負責保存好備忘錄,備忘點較多時,可用List或Stack存儲。也可以持久化。

代碼

1. Employee.java

 1 // 源發器類
 2 public class Employee {
 3 
 4     private String name;
 5     
 6     private int age;
 7     
 8     private double salary;
 9 
10     public Employee(String name, int age, double salary) {
11         this.name = name;
12         this.age = age;
13         this.salary = salary;
14     }
15     
16     // 備忘
17     public EmployeeMemento memento() {
18         return new EmployeeMemento(this);
19     }
20     
21     // 恢復
22     public void recover(EmployeeMemento employeeMemento) {
23         this.name = employeeMemento.getName();
24         this.age = employeeMemento.getAge();
25         this.salary = employeeMemento.getSalary();
26     }
27 
28     public String getName() {
29         return name;
30     }
31 
32     public void setName(String name) {
33         this.name = name;
34     }
35 
36     public int getAge() {
37         return age;
38     }
39 
40     public void setAge(int age) {
41         this.age = age;
42     }
43 
44     public double getSalary() {
45         return salary;
46     }
47 
48     public void setSalary(double salary) {
49         this.salary = salary;
50     }
51     
52     @Override
53     public String toString() {
54         return "name=" + name + ",age=" + age + ",salary=" + salary;
55     }
56     
57 }
View Code

2. EmployeeMemento.java

 1 public class EmployeeMemento {
 2 
 3     private String name;
 4 
 5     private int age;
 6 
 7     private double salary;
 8 
 9     public EmployeeMemento(Employee employee) {
10         this.name = employee.getName();
11         this.age = employee.getAge();
12         this.salary = employee.getSalary();
13     }
14 
15     public String getName() {
16         return name;
17     }
18 
19     public int getAge() {
20         return age;
21     }
22 
23     public double getSalary() {
24         return salary;
25     }
26     
27 }
View Code

3. EmployeeCareTaker.java

 1 import java.util.ArrayList;
 2 import java.util.List;
 3 
 4 public class EmployeeCareTaker {
 5 
 6     private List<EmployeeMemento> mementoes = new ArrayList<>();
 7     
 8     public void addMemento(EmployeeMemento mementoe) {
 9         mementoes.add(mementoe);
10     }
11     
12     public EmployeeMemento getMemento(int index) {
13         return mementoes.get(index);
14     }
15     
16     public EmployeeMemento getLastMemento() {
17         return getMemento(mementoes.size() - 1);
18     }
19     
20 }
View Code

4. Client.java

 1 public class Client {
 2 
 3     public static void main(String[] args) {
 4         EmployeeCareTaker careTaker = new EmployeeCareTaker();
 5         
 6         Employee employee = new Employee("張三", 18, 1000);
 7         System.out.println(employee);
 8         
 9         careTaker.addMemento(employee.memento());
10         employee.setAge(20);
11         employee.setSalary(3000);
12         System.out.println(employee);
13         careTaker.addMemento(employee.memento());
14         
15         employee.setAge(21);
16         System.out.println(employee);
17         
18         employee.recover(careTaker.getLastMemento());
19         System.out.println(employee);
20     }
21 
22 }
View Code

應用場景

1. 棋類游戲的悔棋。

2. 編輯軟件的撤銷操作。

3. 數據庫的事務回滾操作。

4. Photoshop的歷史版本記錄。

 

作者:netoxi
出處:http://www.cnblogs.com/netoxi
本文版權歸作者和博客園共有,歡迎轉載,未經同意須保留此段聲明,且在文章頁面明顯位置給出原文連接。歡迎指正與交流。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM