設計模式--觀察者模式初探和java Observable模式


初步認識觀察者模式

  觀察者模式又稱為發布/訂閱(Publish/Subscribe)模式,因此我們可以用報紙期刊的訂閱來形象的說明:

    報社方負責出版報紙.

    你訂閱了該報社的報紙,那么只要報社發布了新報紙,就會通知你,或發到你手上.

    如果你不想再讀報紙,可以取消訂閱,這樣,報社發布了新報紙就不會再通知你.

  理解其實以上的概念,就可以理解觀察者模式,觀察者模式中有主題(Subject)和觀察者(Observer),分別對應報社和訂閱用戶(你).觀察者模式定義了對象之間的一對多的依賴關系,這樣,當"一"的一方狀態發生變化時,它所依賴的"多"的一方都會收到通知並且自動更新.如圖:

     

實現觀察者模式

  這里以師生關系為例,老師和學生是一對多的關系,老師給學生布置作業,這個動作作為主題事件,每當老師布置一道題時,就要自動通知到所有的學生把該題記下來,然后再布置下一道題...

  這是一個並不標准的類圖,主題接口和實現類中,一般需要有addObserver(),deleteObserver(),notifyObserver();用來注冊刪除觀察者以及在主題狀態發生變化時通知所有的觀察者對象.

  接下來進行代碼實現:

  Subject接口:

package com.wang.observer; //主題接口
 interface Subject { //添加觀察者
     void addObserver(Observer obj); //移除觀察者
     void deleteObserver(Observer obj); //當主題方法改變時,這個方法被調用,通知所有的觀察者
     void notifyObserver(); }

  Oserver接口:

package com.wang.observer; interface Observer { //當主題狀態改變時,會將一個String類型字符傳入該方法的參數,每個觀察者都需要實現該方法
    public void update(String info); }

Subject接口實現類TeacherSubject:

package com.wang.observer; import java.util.ArrayList; import java.util.List; public class TeacherSubject implements Subject { //用來存放和記錄觀察者
    private List<Observer> observers=new ArrayList<Observer>(); //記錄狀態的字符串
    private String info; @Override public void addObserver(Observer obj) { observers.add(obj); } @Override public void deleteObserver(Observer obj) { int i = observers.indexOf(obj); if(i>=0){ observers.remove(obj); } } @Override public void notifyObserver() { for(int i=0;i<observers.size();i++){ Observer o=(Observer)observers.get(i); o.update(info); } } //布置作業的方法,在方法最后,需要調用notifyObserver()方法,通知所有觀察者更新狀態
    public void setHomework(String info){ this.info=info; System.out.println("今天的作業是"+info); this.notifyObserver(); } }

Observer接口實現類StudentObserver:

package com.wang.observer; public class StudentObserver implements Observer { //保存一個Subject的引用,以后如果可以想取消訂閱,有了這個引用會比較方便 private TeacherSubject t; //學生的姓名,用來標識不同的學生對象
    private String name; //構造器用來注冊觀察者
    public Student(String name,Teacher t) { this.name=name; this.t = t; //每新建一個學生對象,默認添加到觀察者的行列
        t.addObserver(this); } @Override public void update(String info) { System.out.println(name+"得到作業:"+info); } }

測試類TestObserver:

package com.wang.observer; public class TestObserver { public static void main(String[] args) { TeacherSubject teacher=new TeacherSubject(); StudentObserver zhangSan=new StudentObserver("張三", teacher); StudentObserver LiSi=new StudentObserver("李四", teacher); StudentObserver WangWu=new StudentObserver("王五", teacher); teacher.setHomework("第二頁第六題"); teacher.setHomework("第三頁第七題"); teacher.setHomework("第五頁第八題"); } }

打印結果:

今天的作業是第二頁第六題
張三得到作業:第二頁第六題
李四得到作業:第二頁第六題
王五得到作業:第二頁第六題
今天的作業是第三頁第七題
張三得到作業:第三頁第七題
李四得到作業:第三頁第七題
王五得到作業:第三頁第七題
今天的作業是第五頁第八題
張三得到作業:第五頁第八題
李四得到作業:第五頁第八題
王五得到作業:第五頁第八題

從打印結果看,每當老師布置作業的狀態改變,就會通知每一個學生.以上就是一個簡單的觀察者模式的實現.

java內置的觀察者模式:

  在java.util包中包含有基本的Observer接口和Observable抽象類.功能上和Subject接口和Observer接口類似.不過在使用上,就方便多了,因為許多功能比如說注冊,刪除,通知觀察者的那些功能已經內置好了.

  使用javaAPI的觀察者模式需要明白這么幾件事情:

   如何使對象變為觀察者?

    實現觀察者接口(java.util.Observer),然后調用Observable對象的addObserver()方法.不想再當觀察者時,調用deleteObserver()就可以了.

   被觀察者(主題)如何發出通知?

    第一步:先調用setChanged()方法,標識狀態已經改變的事實.

    第二步:調用notifyObservers()方法或者notifyObservers(Object arg),這就牽扯到推(push)和拉(pull)的方式傳送數據.如果想用push的方式"推"數據給觀察者,可以把數據當做數據對象傳送給notifyObservers(Object arg)方法,其中的arg可以為任意對象,意思是你可以將任意對象傳送給每一個觀察者.如果調用不帶參數的notifyObserver()方法,則意味着你要使用pull的方式去主題對象中"拉"來所需要的數據.

   觀察者如何接收通知?

     觀察者只需要實現一個update(Observable o,Object arg)方法,第一個參數o,是指定通知是由哪個主題下達的,第二個參數arg就是上面notifyObserver(Object arg)里傳入的數據,如果不傳該值,arg為null.

  下面使用java內置API實現上面我所寫的老師和學生的例子:

被觀察者TeacherSubject:

package com.wang.observer1; import java.util.Observable; public class Teacher extends Observable { //布置作業的狀態信息字符串
    private String info; public void setHomework(String info) { this.info=info; System.out.println("布置的作業是"+info); setChanged(); notifyObservers(); } public String getInfo() { return info; } }

觀察者StudentObserver:

package com.wang.observer1; import java.util.Observable; import java.util.Observer; public class Student implements Observer{ private Observable ob; private String name; public Student(String name,Observable ob) { this.ob = ob; this.name=name; ob.addObserver(this); } @Override public void update(Observable o, Object arg) { Teacher t=(Teacher)o; System.out.println(name+"得到作業信息:"+t.getInfo()); } }

  測試代碼和打印結果我就不再寫了,和上面的例子是一樣一樣的,在這個例子中我使用的是"pull"的方式拉數據,在需要傳遞狀態的TeacherSubject中定義了一個info字符串的get方法,在觀察者對象中調用get方法得到所需數據,如果希望使用push的方式,只需要在TeacherSubject類的notifyOservers()方法中傳入String類型的info字符串即可在update()方法中直接通過第二個參數獲取到arg,即使前面傳過來的info字符串.

 觀察者模式的好處

  觀察者模式提供了一種對象設計,讓主題和觀察者之間耦合度降得很低,為什么呢?關於觀察者的一切,主題只知道觀察者實現了Observer接口,並不需要觀察者具體的類是誰,做了什么或者其他細節.

  這樣的話,由於松耦合,改變主題或者觀察者其中一方,並不會影響另一方,只要他們之間的接口仍被遵守,就可以自由地改變它.

  降低對象之間的耦合度,也是面設對象設計的一個很重要的原則.  

 


免責聲明!

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



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