觀察者模式是軟件設(shè)計(jì)模式中的一種,使用也比較普遍,尤其是在GUI編程中。關(guān)于設(shè)計(jì)模式的文章,網(wǎng)絡(luò)上寫的都比較多,而且很多文章寫的也不錯(cuò),雖然說有一種重復(fù)早輪子的嫌疑,但此輪子非彼輪子,側(cè)重點(diǎn)不同,思路也不同,講述方式也不近相同。
關(guān)鍵要素
主題:
主題是觀察者觀察的對(duì)象,一個(gè)主題必須具備下面三個(gè)特征。
- 持有監(jiān)聽的觀察者的引用
- 支持增加和刪除觀察者
- 主題狀態(tài)改變,通知觀察者
觀察者:
當(dāng)主題發(fā)生變化,收到通知進(jìn)行具體的處理是觀察者必須具備的特征。
為什么要用這種模式
這里舉一個(gè)例子來說明,牛奶送奶站就是主題,訂奶客戶為監(jiān)聽者,客戶從送奶站訂閱牛奶后,會(huì)每天收到牛奶。如果客戶不想訂閱了,可以取消,以后就不會(huì)收到牛奶。
松耦合
觀察者增加或刪除無需修改主題的代碼,只需調(diào)用主題對(duì)應(yīng)的增加或者刪除的方法即可。
主題只負(fù)責(zé)通知觀察者,但無需了解觀察者如何處理通知。舉個(gè)例子,送奶站只負(fù)責(zé)送遞牛奶,不關(guān)心客戶是喝掉還是洗臉。
觀察者只需等待主題通知,無需觀察主題相關(guān)的細(xì)節(jié)。還是那個(gè)例子,客戶只需關(guān)心送奶站送到牛奶,不關(guān)心牛奶由哪個(gè)快遞人員,使用何種交通工具送達(dá)。
Java實(shí)現(xiàn)觀察者模式
1. Java自帶的實(shí)現(xiàn)
類圖
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
|
/** * 觀察目標(biāo) 繼承自 java.util.Observable * @author stone * */ public class UpdateObservable extends Observable { private int data; public UpdateObservable(Observer observer) { addObserver(observer); /* * add other observer */ } public int getData() { return data; } public void setData( int data) { if (data != this .data) { this .data = data; setChanged(); //標(biāo)記 改變, 只有標(biāo)記后才能通知到 notifyObservers(); //通知 } } @Override public synchronized void addObserver(Observer o) { super .addObserver(o); } @Override public synchronized void deleteObserver(Observer o) { super .deleteObserver(o); } @Override public void notifyObservers() { super .notifyObservers(); } @Override public void notifyObservers(Object arg) { super .notifyObservers(arg); } @Override public synchronized void deleteObservers() { super .deleteObservers(); } @Override protected synchronized void setChanged() { super .setChanged(); } @Override protected synchronized void clearChanged() { super .clearChanged(); } @Override public synchronized boolean hasChanged() { return super .hasChanged(); } @Override public synchronized int countObservers() { return super .countObservers(); } } |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
/** * 觀察者 實(shí)現(xiàn) java.util.Observer接口 * @author stone * */ public class UpdateObserver implements Observer { @Override public void update(Observable o, Object arg) { System.out.println( "接收到數(shù)據(jù)變化的通知:" ); if (o instanceof UpdateObservable) { UpdateObservable uo = (UpdateObservable) o; System.out.print( "數(shù)據(jù)變更為:" + uo.getData()); } } } |
2. 自定義的觀察模型
類圖
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
/** * 抽象觀察者 Observer * 觀察 更新 * @author stone * */ public interface IWatcher { /* * 通知接口: * 1. 簡單通知 * 2. 觀察者需要目標(biāo)的變化的數(shù)據(jù),那么可以將目標(biāo)用作參數(shù), 見Java的Observer和Observable */ // void update(IWatched watched); void update(); } |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
/** * 抽象目標(biāo) Subject * 提供注冊(cè)和刪除觀察者對(duì)象的接口, 及通知觀察者進(jìn)行觀察的接口 * 及目標(biāo) 自身被觀察的業(yè)務(wù)的接口 * @author stone * */ public interface IWatchedSubject { public void add(IWatcher watch); public void remove(IWatcher watch); public void notifyWhatchers(); public void update(); //被觀察業(yè)務(wù)變化的接口 } |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
/** * 具體觀察者 Concrete Observer * * @author stone * */ public class UpdateWatcher implements IWatcher { @Override public void update() { System.out.println( this + "觀察到:目標(biāo)已經(jīng)更新了" ); } } |
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
|
/** * 具體目標(biāo)角色 Concrete Subject * @author stone * */ public class UpdateWatchedSubject implements IWatchedSubject { private List<IWatcher> list; public UpdateWatchedSubject() { this .list = new ArrayList<IWatcher>(); } @Override public void add(IWatcher watch) { this .list.add(watch); } @Override public void remove(IWatcher watch) { this .list.remove(watch); } @Override public void notifyWhatchers() { for (IWatcher watcher : list) { watcher.update(); } } @Override public void update() { System.out.println( "目標(biāo)更新中...." ); notifyWhatchers(); } } |
監(jiān)聽器是觀察者的一種實(shí)現(xiàn):
類圖
1
2
3
4
5
6
7
8
|
/** * 監(jiān)聽 用戶在注冊(cè)后 * @author stone * */ public interface IRegisterListener { void onRegistered(); } |
1
2
3
4
5
6
7
8
|
/** * 監(jiān)聽 當(dāng)用戶登錄后 * @author stone * */ public interface ILoginListener { void onLogined(); } |
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
|
/* * 監(jiān)聽器 是觀察者模式的一種實(shí)現(xiàn) * 一些需要監(jiān)聽的業(yè)務(wù)接口上添加 監(jiān)聽器,調(diào)用監(jiān)聽器的相應(yīng)方法,實(shí)現(xiàn)監(jiān)聽 */ public class User { public void register(IRegisterListener register) { /* * do ... register */ System.out.println("正在注冊(cè)中..."); //注冊(cè)后 register.onRegistered(); } public void login(ILoginListener login) { /* * do ... login */ System.out.println( "正在登錄中..." ); //登錄后 login.onLogined(); } } |
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
|
/** * 觀察者(Observer)模式 行為型模式 * 觀察者模式定義了一種一對(duì)多的依賴關(guān)系,讓多個(gè)觀察者對(duì)象同時(shí)觀察某一個(gè)目標(biāo)對(duì)象。 * 這個(gè)目標(biāo)對(duì)象在狀態(tài)上發(fā)生變化時(shí),會(huì)通知所有觀察者對(duì)象,讓它們能夠自動(dòng)更新自己 * 目標(biāo)對(duì)象中需要有添加、移除、通知 觀察者的接口 * * @author stone */ public class Test { public static void main(String[] args) { /* * 使用Java自帶的Observer接口和Observable類 */ UpdateObservable observable = new UpdateObservable(new UpdateObserver()); observable.setData(99); System.out.println(""); System.out.println(""); /* * 自定義的觀察者模型 */ IWatchedSubject watched = new UpdateWatchedSubject(); watched.add(new UpdateWatcher()); watched.add(new UpdateWatcher()); watched.update(); System.out.println(""); /* * 子模式-監(jiān)聽器 */ User user = new User(); user.register( new IRegisterListener() { @Override public void onRegistered() { System.out.println( "監(jiān)聽到注冊(cè)后。。。" ); } }); user.login( new ILoginListener() { @Override public void onLogined() { System.out.println( "監(jiān)聽到登錄后。。。" ); } }); } } |
打印
1
2
3
4
5
6
7
8
9
10
11
|
接收到數(shù)據(jù)變化的通知: 數(shù)據(jù)變更為:99 目標(biāo)更新中.... observer.UpdateWatcher@457471e0觀察到:目標(biāo)已經(jīng)更新了 observer.UpdateWatcher@5fe04cbf觀察到:目標(biāo)已經(jīng)更新了 正在注冊(cè)中... 監(jiān)聽到注冊(cè)后。。。 正在登錄中... 監(jiān)聽到登錄后。。。 |