forked from CyC2018/CS-Notes
-
Notifications
You must be signed in to change notification settings - Fork 3
/
設計模式.md
3077 lines (2339 loc) · 79 KB
/
設計模式.md
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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<!-- GFM-TOC -->
* [一、概述](#一概述)
* [二、創建型](#二創建型)
* [1. 單例(Singleton)](#1-單例singleton)
* [2. 簡單工廠(Simple Factory)](#2-簡單工廠simple-factory)
* [3. 工廠方法(Factory Method)](#3-工廠方法factory-method)
* [4. 抽象工廠(Abstract Factory)](#4-抽象工廠abstract-factory)
* [5. 生成器(Builder)](#5-生成器builder)
* [6. 原型模式(Prototype)](#6-原型模式prototype)
* [三、行為型](#三行為型)
* [1. 責任鏈(Chain Of Responsibility)](#1-責任鏈chain-of-responsibility)
* [2. 命令(Command)](#2-命令command)
* [3. 解釋器(Interpreter)](#3-解釋器interpreter)
* [4. 迭代器(Iterator)](#4-迭代器iterator)
* [5. 中介者(Mediator)](#5-中介者mediator)
* [6. 備忘錄(Memento)](#6-備忘錄memento)
* [7. 觀察者(Observer)](#7-觀察者observer)
* [8. 狀態(State)](#8-狀態state)
* [9. 策略(Strategy)](#9-策略strategy)
* [10. 模板方法(Template Method)](#10-模板方法template-method)
* [11. 訪問者(Visitor)](#11-訪問者visitor)
* [12. 空對象(Null)](#12-空對象null)
* [四、結構型](#四結構型)
* [1. 適配器(Adapter)](#1-適配器adapter)
* [2. 橋接(Bridge)](#2-橋接bridge)
* [3. 組合(Composite)](#3-組合composite)
* [4. 裝飾(Decorator)](#4-裝飾decorator)
* [5. 外觀(Facade)](#5-外觀facade)
* [6. 享元(Flyweight)](#6-享元flyweight)
* [7. 代理(Proxy)](#7-代理proxy)
* [參考資料](#參考資料)
<!-- GFM-TOC -->
# 一、概述
設計模式是解決問題的方案,學習現有的設計模式可以做到經驗複用。
擁有設計模式詞彙,在溝通時就能用更少的詞彙來討論,並且不需要了解底層細節。
# 二、創建型
## 1. 單例(Singleton)
### Intent
確保一個類只有一個實例,並提供該實例的全局訪問點。
### Class Diagram
使用一個私有構造函數、一個私有靜態變量以及一個公有靜態函數來實現。
私有構造函數保證了不能通過構造函數來創建對象實例,只能通過公有靜態函數返回唯一的私有靜態變量。
<div align="center"> <img src="pics/eca1f422-8381-409b-ad04-98ef39ae38ba.png"/> </div><br>
### Implementation
#### Ⅰ 懶漢式-線程不安全
以下實現中,私有靜態變量 uniqueInstance 被延遲實例化,這樣做的好處是,如果沒有用到該類,那麼就不會實例化 uniqueInstance,從而節約資源。
這個實現在多線程環境下是不安全的,如果多個線程能夠同時進入 `if (uniqueInstance == null)` ,並且此時 uniqueInstance 為 null,那麼會有多個線程執行 `uniqueInstance = new Singleton();` 語句,這將導致實例化多次 uniqueInstance。
```java
public class Singleton {
private static Singleton uniqueInstance;
private Singleton() {
}
public static Singleton getUniqueInstance() {
if (uniqueInstance == null) {
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
}
```
#### Ⅱ 餓漢式-線程安全
線程不安全問題主要是由於 uniqueInstance 被實例化多次,採取直接實例化 uniqueInstance 的方式就不會產生線程不安全問題。
但是直接實例化的方式也丟失了延遲實例化帶來的節約資源的好處。
```java
private static Singleton uniqueInstance = new Singleton();
```
#### Ⅲ 懶漢式-線程安全
只需要對 getUniqueInstance() 方法加鎖,那麼在一個時間點只能有一個線程能夠進入該方法,從而避免了實例化多次 uniqueInstance。
但是當一個線程進入該方法之後,其它試圖進入該方法的線程都必須等待,即使 uniqueInstance 已經被實例化了。這會讓線程阻塞時間過長,因此該方法有性能問題,不推薦使用。
```java
public static synchronized Singleton getUniqueInstance() {
if (uniqueInstance == null) {
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
```
#### Ⅳ 雙重校驗鎖-線程安全
uniqueInstance 只需要被實例化一次,之後就可以直接使用了。加鎖操作只需要對實例化那部分的代碼進行,只有當 uniqueInstance 沒有被實例化時,才需要進行加鎖。
雙重校驗鎖先判斷 uniqueInstance 是否已經被實例化,如果沒有被實例化,那麼才對實例化語句進行加鎖。
```java
public class Singleton {
private volatile static Singleton uniqueInstance;
private Singleton() {
}
public static Singleton getUniqueInstance() {
if (uniqueInstance == null) {
synchronized (Singleton.class) {
if (uniqueInstance == null) {
uniqueInstance = new Singleton();
}
}
}
return uniqueInstance;
}
}
```
考慮下面的實現,也就是隻使用了一個 if 語句。在 uniqueInstance == null 的情況下,如果兩個線程都執行了 if 語句,那麼兩個線程都會進入 if 語句塊內。雖然在 if 語句塊內有加鎖操作,但是兩個線程都會執行 `uniqueInstance = new Singleton();` 這條語句,只是先後的問題,那麼就會進行兩次實例化。因此必須使用雙重校驗鎖,也就是需要使用兩個 if 語句:第一個 if 語句用來避免 uniqueInstance 已經被實例化之後的加鎖操作,而第二個 if 語句進行了加鎖,所以只能有一個線程進入,就不會出現 uniqueInstance == null 時兩個線程同時進行實例化操作。
```java
if (uniqueInstance == null) {
synchronized (Singleton.class) {
uniqueInstance = new Singleton();
}
}
```
uniqueInstance 採用 volatile 關鍵字修飾也是很有必要的, `uniqueInstance = new Singleton();` 這段代碼其實是分為三步執行:
1. 為 uniqueInstance 分配內存空間
2. 初始化 uniqueInstance
3. 將 uniqueInstance 指向分配的內存地址
但是由於 JVM 具有指令重排的特性,執行順序有可能變成 1>3>2。指令重排在單線程環境下不會出現問題,但是在多線程環境下會導致一個線程獲得還沒有初始化的實例。例如,線程 T<sub>1</sub> 執行了 1 和 3,此時 T<sub>2</sub> 調用 getUniqueInstance() 後發現 uniqueInstance 不為空,因此返回 uniqueInstance,但此時 uniqueInstance 還未被初始化。
使用 volatile 可以禁止 JVM 的指令重排,保證在多線程環境下也能正常運行。
#### Ⅴ 靜態內部類實現
當 Singleton 類被加載時,靜態內部類 SingletonHolder 沒有被加載進內存。只有當調用 `getUniqueInstance()` 方法從而觸發 `SingletonHolder.INSTANCE` 時 SingletonHolder 才會被加載,此時初始化 INSTANCE 實例,並且 JVM 能確保 INSTANCE 只被實例化一次。
這種方式不僅具有延遲初始化的好處,而且由 JVM 提供了對線程安全的支持。
```java
public class Singleton {
private Singleton() {
}
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getUniqueInstance() {
return SingletonHolder.INSTANCE;
}
}
```
#### Ⅵ 枚舉實現
```java
public enum Singleton {
INSTANCE;
private String objName;
public String getObjName() {
return objName;
}
public void setObjName(String objName) {
this.objName = objName;
}
public static void main(String[] args) {
// 單例測試
Singleton firstSingleton = Singleton.INSTANCE;
firstSingleton.setObjName("firstName");
System.out.println(firstSingleton.getObjName());
Singleton secondSingleton = Singleton.INSTANCE;
secondSingleton.setObjName("secondName");
System.out.println(firstSingleton.getObjName());
System.out.println(secondSingleton.getObjName());
// 反射獲取實例測試
try {
Singleton[] enumConstants = Singleton.class.getEnumConstants();
for (Singleton enumConstant : enumConstants) {
System.out.println(enumConstant.getObjName());
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
```
```html
firstName
secondName
secondName
secondName
```
該實現可以防止反射攻擊。在其它實現中,通過 setAccessible() 方法可以將私有構造函數的訪問級別設置為 public,然後調用構造函數從而實例化對象,如果要防止這種攻擊,需要在構造函數中添加防止多次實例化的代碼。該實現是由 JVM 保證只會實例化一次,因此不會出現上述的反射攻擊。
該實現在多次序列化和序列化之後,不會得到多個實例。而其它實現需要使用 transient 修飾所有字段,並且實現序列化和反序列化的方法。
### Examples
- Logger Classes
- Configuration Classes
- Accesing resources in shared mode
- Factories implemented as Singletons
### JDK
- [java.lang.Runtime#getRuntime()](http://docs.oracle.com/javase/8/docs/api/java/lang/Runtime.html#getRuntime%28%29)
- [java.awt.Desktop#getDesktop()](http://docs.oracle.com/javase/8/docs/api/java/awt/Desktop.html#getDesktop--)
- [java.lang.System#getSecurityManager()](http://docs.oracle.com/javase/8/docs/api/java/lang/System.html#getSecurityManager--)
## 2. 簡單工廠(Simple Factory)
### Intent
在創建一個對象時不向客戶暴露內部細節,並提供一個創建對象的通用接口。
### Class Diagram
簡單工廠把實例化的操作單獨放到一個類中,這個類就成為簡單工廠類,讓簡單工廠類來決定應該用哪個具體子類來實例化。
這樣做能把客戶類和具體子類的實現解耦,客戶類不再需要知道有哪些子類以及應當實例化哪個子類。客戶類往往有多個,如果不使用簡單工廠,那麼所有的客戶類都要知道所有子類的細節。而且一旦子類發生改變,例如增加子類,那麼所有的客戶類都要進行修改。
<div align="center"> <img src="pics/40c0c17e-bba6-4493-9857-147c0044a018.png"/> </div><br>
### Implementation
```java
public interface Product {
}
```
```java
public class ConcreteProduct implements Product {
}
```
```java
public class ConcreteProduct1 implements Product {
}
```
```java
public class ConcreteProduct2 implements Product {
}
```
以下的 Client 類包含了實例化的代碼,這是一種錯誤的實現。如果在客戶類中存在這種實例化代碼,就需要考慮將代碼放到簡單工廠中。
```java
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 是簡單工廠實現,它被所有需要進行實例化的客戶類調用。
```java
public class SimpleFactory {
public Product createProduct(int type) {
if (type == 1) {
return new ConcreteProduct1();
} else if (type == 2) {
return new ConcreteProduct2();
}
return new ConcreteProduct();
}
}
```
```java
public class Client {
public static void main(String[] args) {
SimpleFactory simpleFactory = new SimpleFactory();
Product product = simpleFactory.createProduct(1);
// do something with the product
}
}
```
## 3. 工廠方法(Factory Method)
### Intent
定義了一個創建對象的接口,但由子類決定要實例化哪個類。工廠方法把實例化操作推遲到子類。
### Class Diagram
在簡單工廠中,創建對象的是另一個類,而在工廠方法中,是由子類來創建對象。
下圖中,Factory 有一個 doSomething() 方法,這個方法需要用到一個產品對象,這個產品對象由 factoryMethod() 方法創建。該方法是抽象的,需要由子類去實現。
<div align="center"> <img src="pics/f4d0afd0-8e78-4914-9e60-4366eaf065b5.png"/> </div><br>
### Implementation
```java
public abstract class Factory {
abstract public Product factoryMethod();
public void doSomething() {
Product product = factoryMethod();
// do something with the product
}
}
```
```java
public class ConcreteFactory extends Factory {
public Product factoryMethod() {
return new ConcreteProduct();
}
}
```
```java
public class ConcreteFactory1 extends Factory {
public Product factoryMethod() {
return new ConcreteProduct1();
}
}
```
```java
public class ConcreteFactory2 extends Factory {
public Product factoryMethod() {
return new ConcreteProduct2();
}
}
```
### JDK
- [java.util.Calendar](http://docs.oracle.com/javase/8/docs/api/java/util/Calendar.html#getInstance--)
- [java.util.ResourceBundle](http://docs.oracle.com/javase/8/docs/api/java/util/ResourceBundle.html#getBundle-java.lang.String-)
- [java.text.NumberFormat](http://docs.oracle.com/javase/8/docs/api/java/text/NumberFormat.html#getInstance--)
- [java.nio.charset.Charset](http://docs.oracle.com/javase/8/docs/api/java/nio/charset/Charset.html#forName-java.lang.String-)
- [java.net.URLStreamHandlerFactory](http://docs.oracle.com/javase/8/docs/api/java/net/URLStreamHandlerFactory.html#createURLStreamHandler-java.lang.String-)
- [java.util.EnumSet](https://docs.oracle.com/javase/8/docs/api/java/util/EnumSet.html#of-E-)
- [javax.xml.bind.JAXBContext](https://docs.oracle.com/javase/8/docs/api/javax/xml/bind/JAXBContext.html#createMarshaller--)
## 4. 抽象工廠(Abstract Factory)
### Intent
提供一個接口,用於創建 **相關的對象家族** 。
### Class Diagram
抽象工廠模式創建的是對象家族,也就是很多對象而不是一個對象,並且這些對象是相關的,也就是說必須一起創建出來。而工廠方法模式只是用於創建一個對象,這和抽象工廠模式有很大不同。
抽象工廠模式用到了工廠方法模式來創建單一對象,AbstractFactory 中的 createProductA() 和 createProductB() 方法都是讓子類來實現,這兩個方法單獨來看就是在創建一個對象,這符合工廠方法模式的定義。
至於創建對象的家族這一概念是在 Client 體現,Client 要通過 AbstractFactory 同時調用兩個方法來創建出兩個對象,在這裡這兩個對象就有很大的相關性,Client 需要同時創建出這兩個對象。
從高層次來看,抽象工廠使用了組合,即 Cilent 組合了 AbstractFactory,而工廠方法模式使用了繼承。
<div align="center"> <img src="pics/e2190c36-8b27-4690-bde5-9911020a1294.png"/> </div><br>
### Implementation
```java
public class AbstractProductA {
}
```
```java
public class AbstractProductB {
}
```
```java
public class ProductA1 extends AbstractProductA {
}
```
```java
public class ProductA2 extends AbstractProductA {
}
```
```java
public class ProductB1 extends AbstractProductB {
}
```
```java
public class ProductB2 extends AbstractProductB {
}
```
```java
public abstract class AbstractFactory {
abstract AbstractProductA createProductA();
abstract AbstractProductB createProductB();
}
```
```java
public class ConcreteFactory1 extends AbstractFactory {
AbstractProductA createProductA() {
return new ProductA1();
}
AbstractProductB createProductB() {
return new ProductB1();
}
}
```
```java
public class ConcreteFactory2 extends AbstractFactory {
AbstractProductA createProductA() {
return new ProductA2();
}
AbstractProductB createProductB() {
return new ProductB2();
}
}
```
```java
public class Client {
public static void main(String[] args) {
AbstractFactory abstractFactory = new ConcreteFactory1();
AbstractProductA productA = abstractFactory.createProductA();
AbstractProductB productB = abstractFactory.createProductB();
// do something with productA and productB
}
}
```
### JDK
- [javax.xml.parsers.DocumentBuilderFactory](http://docs.oracle.com/javase/8/docs/api/javax/xml/parsers/DocumentBuilderFactory.html)
- [javax.xml.transform.TransformerFactory](http://docs.oracle.com/javase/8/docs/api/javax/xml/transform/TransformerFactory.html#newInstance--)
- [javax.xml.xpath.XPathFactory](http://docs.oracle.com/javase/8/docs/api/javax/xml/xpath/XPathFactory.html#newInstance--)
## 5. 生成器(Builder)
### Intent
封裝一個對象的構造過程,並允許按步驟構造。
### Class Diagram
<div align="center"> <img src="pics/db5e376d-0b3e-490e-a43a-3231914b6668.png"/> </div><br>
### Implementation
以下是一個簡易的 StringBuilder 實現,參考了 JDK 1.8 源碼。
```java
public class AbstractStringBuilder {
protected char[] value;
protected int count;
public AbstractStringBuilder(int capacity) {
count = 0;
value = new char[capacity];
}
public AbstractStringBuilder append(char c) {
ensureCapacityInternal(count + 1);
value[count++] = c;
return this;
}
private void ensureCapacityInternal(int minimumCapacity) {
// overflow-conscious code
if (minimumCapacity - value.length > 0)
expandCapacity(minimumCapacity);
}
void expandCapacity(int minimumCapacity) {
int newCapacity = value.length * 2 + 2;
if (newCapacity - minimumCapacity < 0)
newCapacity = minimumCapacity;
if (newCapacity < 0) {
if (minimumCapacity < 0) // overflow
throw new OutOfMemoryError();
newCapacity = Integer.MAX_VALUE;
}
value = Arrays.copyOf(value, newCapacity);
}
}
```
```java
public class StringBuilder extends AbstractStringBuilder {
public StringBuilder() {
super(16);
}
@Override
public String toString() {
// Create a copy, don't share the array
return new String(value, 0, count);
}
}
```
```java
public class Client {
public static void main(String[] args) {
StringBuilder sb = new StringBuilder();
final int count = 26;
for (int i = 0; i < count; i++) {
sb.append((char) ('a' + i));
}
System.out.println(sb.toString());
}
}
```
```html
abcdefghijklmnopqrstuvwxyz
```
### JDK
- [java.lang.StringBuilder](http://docs.oracle.com/javase/8/docs/api/java/lang/StringBuilder.html)
- [java.nio.ByteBuffer](http://docs.oracle.com/javase/8/docs/api/java/nio/ByteBuffer.html#put-byte-)
- [java.lang.StringBuffer](http://docs.oracle.com/javase/8/docs/api/java/lang/StringBuffer.html#append-boolean-)
- [java.lang.Appendable](http://docs.oracle.com/javase/8/docs/api/java/lang/Appendable.html)
- [Apache Camel builders](https://github.com/apache/camel/tree/0e195428ee04531be27a0b659005e3aa8d159d23/camel-core/src/main/java/org/apache/camel/builder)
## 6. 原型模式(Prototype)
### Intent
使用原型實例指定要創建對象的類型,通過複製這個原型來創建新對象。
### Class Diagram
<div align="center"> <img src="pics/b8922f8c-95e6-4187-be85-572a509afb71.png"/> </div><br>
### Implementation
```java
public abstract class Prototype {
abstract Prototype myClone();
}
```
```java
public class ConcretePrototype extends Prototype {
private String filed;
public ConcretePrototype(String filed) {
this.filed = filed;
}
@Override
Prototype myClone() {
return new ConcretePrototype(filed);
}
@Override
public String toString() {
return filed;
}
}
```
```java
public class Client {
public static void main(String[] args) {
Prototype prototype = new ConcretePrototype("abc");
Prototype clone = prototype.myClone();
System.out.println(clone.toString());
}
}
```
```html
abc
```
### JDK
- [java.lang.Object#clone()](http://docs.oracle.com/javase/8/docs/api/java/lang/Object.html#clone%28%29)
# 三、行為型
## 1. 責任鏈(Chain Of Responsibility)
### Intent
使多個對象都有機會處理請求,從而避免請求的發送者和接收者之間的耦合關係。將這些對象連成一條鏈,並沿著這條鏈發送該請求,直到有一個對象處理它為止。
### Class Diagram
- Handler:定義處理請求的接口,並且實現後繼鏈(successor)
<div align="center"> <img src="pics/ca9f23bf-55a4-47b2-9534-a28e35397988.png"/> </div><br>
### Implementation
```java
public abstract class Handler {
protected Handler successor;
public Handler(Handler successor) {
this.successor = successor;
}
protected abstract void handleRequest(Request request);
}
```
```java
public class ConcreteHandler1 extends Handler {
public ConcreteHandler1(Handler successor) {
super(successor);
}
@Override
protected void handleRequest(Request request) {
if (request.getType() == RequestType.TYPE1) {
System.out.println(request.getName() + " is handle by ConcreteHandler1");
return;
}
if (successor != null) {
successor.handleRequest(request);
}
}
}
```
```java
public class ConcreteHandler2 extends Handler {
public ConcreteHandler2(Handler successor) {
super(successor);
}
@Override
protected void handleRequest(Request request) {
if (request.getType() == RequestType.TYPE2) {
System.out.println(request.getName() + " is handle by ConcreteHandler2");
return;
}
if (successor != null) {
successor.handleRequest(request);
}
}
}
```
```java
public class Request {
private RequestType type;
private String name;
public Request(RequestType type, String name) {
this.type = type;
this.name = name;
}
public RequestType getType() {
return type;
}
public String getName() {
return name;
}
}
```
```java
public enum RequestType {
TYPE1, TYPE2
}
```
```java
public class Client {
public static void main(String[] args) {
Handler handler1 = new ConcreteHandler1(null);
Handler handler2 = new ConcreteHandler2(handler1);
Request request1 = new Request(RequestType.TYPE1, "request1");
handler2.handleRequest(request1);
Request request2 = new Request(RequestType.TYPE2, "request2");
handler2.handleRequest(request2);
}
}
```
```html
request1 is handle by ConcreteHandler1
request2 is handle by ConcreteHandler2
```
### JDK
- [java.util.logging.Logger#log()](http://docs.oracle.com/javase/8/docs/api/java/util/logging/Logger.html#log%28java.util.logging.Level,%20java.lang.String%29)
- [Apache Commons Chain](https://commons.apache.org/proper/commons-chain/index.html)
- [javax.servlet.Filter#doFilter()](http://docs.oracle.com/javaee/7/api/javax/servlet/Filter.html#doFilter-javax.servlet.ServletRequest-javax.servlet.ServletResponse-javax.servlet.FilterChain-)
## 2. 命令(Command)
### Intent
將命令封裝成對象中,具有以下作用:
- 使用命令來參數化其它對象
- 將命令放入隊列中進行排隊
- 將命令的操作記錄到日誌中
- 支持可撤銷的操作
### Class Diagram
- Command:命令
- Receiver:命令接收者,也就是命令真正的執行者
- Invoker:通過它來調用命令
- Client:可以設置命令與命令的接收者
<div align="center"> <img src="pics/c44a0342-f405-4f17-b750-e27cf4aadde2.png"/> </div><br>
### Implementation
設計一個遙控器,可以控制電燈開關。
<div align="center"> <img src="pics/e6bded8e-41a0-489a-88a6-638e88ab7666.jpg"/> </div><br>
```java
public interface Command {
void execute();
}
```
```java
public class LightOnCommand implements Command {
Light light;
public LightOnCommand(Light light) {
this.light = light;
}
@Override
public void execute() {
light.on();
}
}
```
```java
public class LightOffCommand implements Command {
Light light;
public LightOffCommand(Light light) {
this.light = light;
}
@Override
public void execute() {
light.off();
}
}
```
```java
public class Light {
public void on() {
System.out.println("Light is on!");
}
public void off() {
System.out.println("Light is off!");
}
}
```
```java
/**
* 遙控器
*/
public class Invoker {
private Command[] onCommands;
private Command[] offCommands;
private final int slotNum = 7;
public Invoker() {
this.onCommands = new Command[slotNum];
this.offCommands = new Command[slotNum];
}
public void setOnCommand(Command command, int slot) {
onCommands[slot] = command;
}
public void setOffCommand(Command command, int slot) {
offCommands[slot] = command;
}
public void onButtonWasPushed(int slot) {
onCommands[slot].execute();
}
public void offButtonWasPushed(int slot) {
offCommands[slot].execute();
}
}
```
```java
public class Client {
public static void main(String[] args) {
Invoker invoker = new Invoker();
Light light = new Light();
Command lightOnCommand = new LightOnCommand(light);
Command lightOffCommand = new LightOffCommand(light);
invoker.setOnCommand(lightOnCommand, 0);
invoker.setOffCommand(lightOffCommand, 0);
invoker.onButtonWasPushed(0);
invoker.offButtonWasPushed(0);
}
}
```
### JDK
- [java.lang.Runnable](http://docs.oracle.com/javase/8/docs/api/java/lang/Runnable.html)
- [Netflix Hystrix](https://github.com/Netflix/Hystrix/wiki)
- [javax.swing.Action](http://docs.oracle.com/javase/8/docs/api/javax/swing/Action.html)
## 3. 解釋器(Interpreter)
### Intent
為語言創建解釋器,通常由語言的語法和語法分析來定義。
### Class Diagram
- TerminalExpression:終結符表達式,每個終結符都需要一個 TerminalExpression。
- Context:上下文,包含解釋器之外的一些全局信息。
<div align="center"> <img src="pics/2b125bcd-1b36-43be-9b78-d90b076be549.png"/> </div><br>
### Implementation
以下是一個規則檢驗器實現,具有 and 和 or 規則,通過規則可以構建一顆解析樹,用來檢驗一個文本是否滿足解析樹定義的規則。
例如一顆解析樹為 D And (A Or (B C)),文本 "D A" 滿足該解析樹定義的規則。
這裡的 Context 指的是 String。
```java
public abstract class Expression {
public abstract boolean interpret(String str);
}
```
```java
public class TerminalExpression extends Expression {
private String literal = null;
public TerminalExpression(String str) {
literal = str;
}
public boolean interpret(String str) {
StringTokenizer st = new StringTokenizer(str);
while (st.hasMoreTokens()) {
String test = st.nextToken();
if (test.equals(literal)) {
return true;
}
}
return false;
}
}
```
```java
public class AndExpression extends Expression {
private Expression expression1 = null;
private Expression expression2 = null;
public AndExpression(Expression expression1, Expression expression2) {
this.expression1 = expression1;
this.expression2 = expression2;
}
public boolean interpret(String str) {
return expression1.interpret(str) && expression2.interpret(str);
}
}
```
```java
public class OrExpression extends Expression {
private Expression expression1 = null;
private Expression expression2 = null;
public OrExpression(Expression expression1, Expression expression2) {
this.expression1 = expression1;
this.expression2 = expression2;
}
public boolean interpret(String str) {
return expression1.interpret(str) || expression2.interpret(str);
}
}
```
```java
public class Client {
/**
* 構建解析樹
*/
public static Expression buildInterpreterTree() {
// Literal
Expression terminal1 = new TerminalExpression("A");
Expression terminal2 = new TerminalExpression("B");
Expression terminal3 = new TerminalExpression("C");
Expression terminal4 = new TerminalExpression("D");
// B C
Expression alternation1 = new OrExpression(terminal2, terminal3);
// A Or (B C)
Expression alternation2 = new OrExpression(terminal1, alternation1);
// D And (A Or (B C))
return new AndExpression(terminal4, alternation2);
}
public static void main(String[] args) {
Expression define = buildInterpreterTree();
String context1 = "D A";
String context2 = "A B";
System.out.println(define.interpret(context1));
System.out.println(define.interpret(context2));
}