設計模式-觀察者(一)
大家好,我們又見面了,還記得上次所寫的 <<設計模式-策略模式>>麼?,我們今天來學習另一個模式之前,先複習一下上次的”策略模式”是怎麼定義的吧:
定義:
策略模式定義
:算法族,分別封裝起來,讓他們之間可以互相替換,次模式讓算法的變化獨立於使用算法的客戶.
“策略模式”中我們還學到了兩個設計原則:
- 1 :找出應用中可能需要變化之處,把他們獨立起來,不要和那些不需要變化的部分的代碼混合在一起.
- 2 :針對藉口編程,而不是針對實現編程.
思考……………
經過對”策略模式”的思考,是不是有點印象了? 下面我們繼續向下一個模式進發把……..
這次我們先推出這個模式,名字爲”觀察者模式”, 它的定義
: 觀察者模式定義了對象之間的一對多依賴,這樣一來,當一個對象改變狀態時,它的所有依賴者都會收到通知並自動更新.
不懂?
當然,如果能現在就能明白其真諦的話,你可以關瀏覽器了,以後都不需要再來GC博客了……(開玩笑 ^_^)
我們一步一步來,先了解一下什麼是觀察者模式.
*不知道你訂沒訂閱報紙(如果你從小到大沒訂閱過報紙或者書刊雜誌,我只能說你錯過了一個生活體驗..) 好吧,瞎扯了,我們先介紹一下訂閱報刊的過程:
假如你家附近有一所報社,而你又是<<意林>>(高中生應該都讀過的)的愛好者,你當然會迫不及待的去訂閱一年屬於自己的<<意林>>.當你去報社訂閱了<<意林>>之後,每當報社收到新出版的<<意林>>,它就會將你的那份送到你的家中供你閱讀.*
好吧.整個流程應該弄明白了吧,是在不明白可以親自去訂閱一年的<<意林>>體驗一把哈.
到這裏,可能你會想:訂閱報刊與”觀察者模式”有什麼關係啊~
我只能回答你:其實訂閱報刊整個流程就是"觀察者模式"的整個流程
,也就是說"訂閱報刊"用了"觀察者模式"
.
當然,如果你已經想到了,那麼只能說你的智商真的是太高了(@_@)
報社就相當於”觀察者模式”裏的”發佈者(publish)/主題(Subject)”,而你就相當於”觀察者模式”裏的”訂閱者(Subscribe)/觀察者(Observer)”
“觀察者模式”介紹完畢~~~
騙你的,代碼還沒寫,怎麼可能介紹完畢呢.況且”觀察者模式”有好多種,今天只介紹其一….
現在我們開始寫代碼吧…用代碼來實現上面的”訂閱報刊”的功能…
//定義報社類
public class NewspaperOffice {
//定義主題後期會更新的數據
private int updatedata;
//通知所有的<<意林>>訂閱者
@Override
public void notifyObservers() {
//訂閱者1訂閱了意林
Subscriber1 s1 = new Subscriber1();
//讓訂閱者1更新數據,也就是給訂閱者1送去<<意林>>
s1.update(this.updatedata);
//訂閱者2訂閱了意林
Subscriber1 s2 = new Subscriber2();
//讓訂閱者2更新數據,也就是給訂閱者2送去<<意林>>
s2.update(this.updatedata);
}
//當主題裏的數據進行更新的時候,通知所有的觀察者
public void setUpdatedata(int updatedata) {
this.updatedata = updatedata;
notifyObservers();
}
}
//定義訂閱者1類
public class Subscriber1 {
//定義訂閱者自己的信息
private String name;
//定義訂閱者需要在報社那裏接到的數據
private int updatedata;
//構造器裏還裝入了該訂閱者的名字
public Subscriber1(String name){
this.name = name;
}
public void display() {
System.out.println("我是訂閱者,我的名字叫: "+name+" , 這是從報社接到的數據: "+ updatedata);
}
public void update(int updatedata) {
this.updatedata = updatedata;
display();
}
}
//定義訂閱者2類
public class Subscriber2 {
//定義訂閱者自己的信息
private String name;
private int age;
//定義訂閱者需要在報社那裏接到的數據
private int updatedata;
//構造器裏還裝入了該訂閱者的名字
public Subscriber1(String name, int age){
this.name = name;
}
//顯示出信息
public void display() {
System.out.println("我是訂閱者,我的名字叫: "+name+", 我的年齡是: "+age+" , 這是從報社接到的數據: "+ updatedata);
}
//更新訂閱者手中的信息(也就是更新手中的<<意林>>書的個數)
public void update(int updatedata) {
this.updatedata = updatedata;
display();
}
}
看了上面的代碼,你有沒有一種想罵我的感覺,上面的代碼太不靈活了,當然這不可能是”觀察者模式”哈,我只是寫了一個java初學者
會設計的模式.
上面代碼很不靈活,的確.如果有一天,又有一個”訂閱者3”去報刊訂閱<<意林>>,NewspaperOffice
這個類就需要重新寫代碼,在notifyObservers()
方法裏添加”訂閱者3”的實例…當有一天”訂閱者2”不想訂閱了,NewspaperOffice
類又要重新重構代碼.麻煩至極啊…
很顯然啊,上面的代碼太別沒有遵循之間我們所說的兩個設計原則,所有是一組極差,及其不具備靈活性的代碼…
下面,我們用”觀察者模式”編寫上面的代碼…
首先,我們需要面向接口編程
,把NewspaperOffice
與 Subscriber1
,Subscriber2
都提取一下.
/*
這裏提供了一個Subject(主題)接口,接口裏定義了:註冊觀察者方法,刪除觀察者方法,向已經註冊的觀察者發送消息的方法
*/
public interface Subject {
//用來註冊觀察者
public void registerObserver(Observer o);
//用來刪除觀察者
public void removeObserver(Observer o);
//通知所有已經註冊的觀察者
public void notifyObservers();
}
/*
定義Observer(觀察者)接口,所有觀察者都具備更新內部數據的功能
*/
public interface Observer {
//更新觀察者內部的數據
public void update(int updatedata);
}
/*
定義Subscriber(訂閱者)接口,所有訂閱者都可以展出自己的內部信息(也就是所有訂閱者都可以向別人展出手中意林數目
*/
public interface Subscriber {
//每一個訂閱者都可以顯示自己的信息
public void display();
}
/*
下面,我們的報社出廠了
*/
import java.util.ArrayList;
import java.util.List;
//報社對象實線了主題藉口
public class NewspaperOffice implements Subject {
//定義主題後期會更新的數據
private int updatedata;
//建立一個list來裝註冊進來的觀察者
private List <Observer>observers;
//構造函數,構造list
public NewspaperOffice(){
observers = new ArrayList<Observer>();
}
//添加註冊觀察者
@Override
public void registerObserver(Observer o) {
observers.add(o);
}
//移除觀察者
@Override
public void removeObserver(Observer o) {
int i = observers.indexOf(o);
if(i >= 0){
observers.remove(i);
}
}
//通知所有的觀察者
@Override
public void notifyObservers() {
for(Observer observer: observers){
observer.update(this.updatedata);
}
}
//當主題裏的數據進行更新的時候,通知所有的觀察者
public void setUpdatedata(int updatedata) {
this.updatedata = updatedata;
notifyObservers();
}
}
/*
定義訂閱者1 實現了觀察者接口,訂閱者的接口
*/
public class Subscriber1 implements Observer, Subscriber {
//定義訂閱者自己的信息
private String name;
//定義訂閱者需要在報社那裏接到的數據
private int updatedata;
//定義觀察者引用
private Subject subject;
//構造器裏接受到該觀察者需要註冊的到哪個主題。並且註冊到該主題上
//構造器裏還裝入了該訂閱者的名字
public Subscriber1(Subject subject, String name){
this.name = name;
this.subject = subject;
subject.registerObserver(this);
}
@Override
public void display() {
System.out.println("我是訂閱者,我的名字叫: "+name+" , 這是從報社接到的數據: "+ updatedata);
}
@Override
public void update(int updatedata) {
this.updatedata = updatedata;
display();
}
}
/*
定義訂閱者2,基本內容與訂閱者1相似,但是請注意,訂閱者1與訂閱者2是完全不同的兩個類型,因爲訂閱者2裏需要填寫年齡信息.
如果兩個類一模一樣,那寫成一個類不就好了麼?
爲什麼要這樣設計兩個類呢? 因爲我們後期的訂閱者是各種各樣的,訂閱者怎麼變化都無所謂,這充分說明了我們類與類之間的耦合程度非常低.
*/
public class Subscriber2 implements Observer, Subscriber {
//定義訂閱者自己的信息
private String name;
private int age;
//定義訂閱者需要在報社那裏接到的數據
private int updatedata;
//定義觀察者引用
private Subject subject;
//構造器裏接受到該觀察者需要註冊的到哪個主題。並且註冊到該主題上
//構造器裏還裝入了該訂閱者的名字
public Subscriber2(Subject subject, String name, int age){
this.age = age;
this.name = name;
this.subject = subject;
subject.registerObserver(this);
}
@Override
public void display() {
System.out.println("我是訂閱者,我的名字叫: "+name+", 我的年齡是: "+age+" , 這是從報社接到的數據: "+ updatedata);
}
@Override
public void update(int updatedata) {
this.updatedata = updatedata;
display();
}
}
基本結構都已經設計完了,現在我來寫個測試類進行測試
public class Main {
public static void main(String[] args) {
//創建報社
NewspaperOffice nsp1 = new NewspaperOffice();
//用Subscriber1創建訂閱者1,並且將他註冊到報社
Subscriber1 GC = new Subscriber1(nsp1, "GC");
//用Subscriber2創建訂閱者2,也註冊到報社
Subscriber2 YP = new Subscriber2(nsp1, "YP", 33);
System.out.println("===========下面開始發佈第1版報紙,此時GC與YP都在報社裏========");
//現在報社開始發佈第一版報紙
nsp1.setUpdatedata(1);
System.out.println("===========下面開始發佈第2版報紙,此時GC與YP都在報社裏========");
//報社發佈第二版報紙
nsp1.setUpdatedata(2);
//現在GC不訂閱報紙了,報社刪除GC
nsp1.removeObserver(GC);
System.out.println("===========下面開始發佈第3版報紙,此時只有YP都在報社裏========");
//刪除GC之後,報社又發佈了第三版報紙
nsp1.setUpdatedata(3);
/*
* 運行結果如下:
* ===========下面開始發佈第1版報紙,此時GC與YP都在報社裏========
* 我是訂閱者,我的名字叫: GC , 這是從報社接到的數據: 1
* 我是訂閱者,我的名字叫: YP, 我的年齡是: 33 , 這是從報社接到的數據: 1
* ===========下面開始發佈第2版報紙,此時GC與YP都在報社裏========
* 我是訂閱者,我的名字叫: GC , 這是從報社接到的數據: 2
* 我是訂閱者,我的名字叫: YP, 我的年齡是: 33 , 這是從報社接到的數據: 2
* ===========下面開始發佈第3版報紙,此時只有YP都在報社裏========
* 我是訂閱者,我的名字叫: YP, 我的年齡是: 33 , 這是從報社接到的數據: 3
*/
}
}
這是“觀察者”模式的類圖,裏面只有Subject類與Observer類之間的關係,請仔細思考。
好吧,我承認,”觀察者模式”不是那麼容易理解,如果你看了我上面所寫的內容之後就的能理解”觀察者模式”,我真的再次讚歎一下:你的智商真的很高很高….
經過對將近兩個小時的奮鬥,終於把<<設計模式-觀察者模式>>寫完了,如果有不會的,沒看懂的請Email
或者留言
給我……………
請轉發OR複製的同學,標註出處
,尊重作者勞動成果
,謝謝親
謝謝觀看,結束本節!
注:本文借鑑了《HeadFirst-設計模式》一書,作者總結的內容不到書內容精彩十分之一,還望大家多讀原著。