天天看點

觀察者模式(Observer)1. 問題場景2. UML圖3. 具體代碼實作代碼:4. 研磨設計模式

1. 問題場景

當一個對象的狀态放生改變的時候,如何讓依賴于它的所有對象得到通知,并進行相應的處理?

2. UML圖

觀察者模式(Observer)1. 問題場景2. UML圖3. 具體代碼實作代碼:4. 研磨設計模式

Subject:目标對象,通常具有以下功能

  • 一個目标對象可以被多個觀察者觀察
  • 目标提供觀察者注冊和退訂的維護
  • 當目标狀态發生改變時,目标負責通知所有注冊的、有效的觀察者

Observer:定義觀察者接口,提供目标通知時對應的更新方法,這個更新方法進行相應的業務邏輯處理,可以在這個方法回調目标對象,以擷取目标對象的資料

ConcreteSubject:具體的目标對象,用來維護目标的狀态,當目标狀态發生改變時,通知所有注冊有效的觀察者,讓觀察者執行相應的處理

ConcreteObserver:觀察者的具體對象,用來接收目标的通知,并進行相應的後續處理

3. 具體代碼實作代碼:

// Subject:public class Subject {
    //注意:Arraylist裡面可以添加null元素
    private List<Observer> readers = new ArrayList<>();    public void attach(Observer reader){        if(reader != null){
            readers.add(reader);
        }
    }    public void detach(Observer reader){        if(reader != null){
            readers.remove(reader);
        }
    }    public void notifyAllReaders(){        if(readers.size() != 0){            //通過流的方式來通路
            //因為是從Newapaper方法裡調用,所有this表示Newspaper執行個體
            readers.forEach(reader -> reader.update(this));
        }

    }
}//Newspaper:public class Newspaper extends  Subject{
    private String content;    public String getContent(){        return content;
    }    //維護目标的狀态
    public void setContent(String content){        //内容更新之後通知所有觀察者
        this.content = content;
        notifyAllReaders();
    }
}// Observer:public interface Observer {
    public void update(Subject subject);
}// Reader:public class Reader implements Observer {

    private String name;    public void setName(String name) {        this.name = name;
    }    public String getName() {        return name;
    }    @Override
    public void update(Subject subject) {
        System.out.println(name + "收到了報紙\n 報紙的内容為: " 
                                         + ((Newspaper)subject).getContent() );
    }
}//Client端public class Client {
    public static void main(String[] args) {       //建立一個報紙,作為被觀察者
       NewsPaper subject = new NewsPaper();       //建立閱讀者,也就是觀察者
       Reader reader1 = new Reader();
       reader1.setName("張三");

       Reader reader2 = new Reader();
       reader2.setName("李四");

       Reader reader3 = new Reader();
       reader3.setName("王五");       //注冊閱讀者
       subject.attach(reader1);
       subject.attach(reader2);
       subject.attach(reader3);       //要出報紙啦
       subject.setContent("本期内容是觀察者模式");
    }
}           

複制

4. 研磨設計模式

觀察者模式的定義:

定義對象間的一種一對多的依賴關系,當一個對象的狀态發生改變時,所有依賴于它的對象都得到通知并被自動更新。

觀察者模式把多個訂閱者稱為觀察者Observer,多個觀察者觀察的對象的被稱為目标Subject。

一個目标可以有任意多個觀察者對象,一旦目标的狀态發生改變時,所有注冊的觀察者就會得到通知,然後各個觀察者會對通知作出相應的處理,執行相應的業務功能處理,并使自己的狀态和目标對象的狀态保持一緻。

觀察者模式的本質:觸發關聯

Swing中的觀察者模式:Swing元件是被觀察的目标,而每個實作監聽的類就是觀察者,監聽器的接口就是觀察者的接口,在調用addXXXListener方法的時候就相當于注冊觀察者。當元件被單擊時,狀态發生改變的時候,就會産生相應的通知,會調用注冊的觀察者的方法,就是我們所實作的監聽器的方法。

擴充:差別對待觀察者

/**
 * 定義水質監測的目标對象
 */public abstract class WaterQualitySubject {
    /**
     * 用來儲存注冊的觀察者對象
     */
    protected List<WatcherObserver> observers = new ArrayList<WatcherObserver>();    /**
     * 注冊觀察者對象
     * @param observer 觀察者對象
     */
    public void attach(WatcherObserver observer) {
        observers.add(observer);
    }    /**
     * 删除觀察者對象
     * @param observer 觀察者對象
     */
    public void detach(WatcherObserver observer) {
        observers.remove(observer);
    }    /**
     * 通知相應的觀察者對象
     */
    public abstract void notifyWatchers();    /**
     * 擷取水質污染的級别
     * @return 水質污染的級别
     */
    public abstract int getPolluteLevel();
}/**
 * 具體的水質監測對象
 */public class WaterQuality extends WaterQualitySubject{
    /**
     * 污染的級别,0表示正常,1表示輕度污染,2表示中度污染,3表示高度污染
     */
    private int polluteLevel = 0;    /**
     * 擷取水質污染的級别
     * @return 水質污染的級别
     */
    public int getPolluteLevel() {        return polluteLevel;
    }    /**
     * 當監測水質情況後,設定水質污染的級别
     * @param polluteLevel 水質污染的級别
     */
    public void setPolluteLevel(int polluteLevel) {        this.polluteLevel = polluteLevel;        //通知相應的觀察者
        this.notifyWatchers();
    }    /**
     * 通知相應的觀察者對象
     */
    public void notifyWatchers() {        //循環所有注冊的觀察者
        for(WatcherObserver watcher : observers){                        //開始根據污染級别判斷是否需要通知,由這裡總控
                        if(this.polluteLevel >= 0){                            //通知監測員做記錄
                            if("監測人員".equals(watcher.getJob())){
                                watcher.update(this);
                            }
                        }                        if(this.polluteLevel >= 1){                            //通知預警人員
                            if("預警人員".equals(watcher.getJob())){
                                watcher.update(this);
                            }
                        }                        if(this.polluteLevel >= 2){                            //通知監測部門上司
                           if("監測部門上司".equals(watcher.getJob())){
                                watcher.update(this);
                            }
                        }
        }
    }
}/**
 * 水質觀察者接口定義
 */public interface WatcherObserver {
    /**
     * 被通知的方法
     * @param subject 傳入被觀察的目标對象
     */
    public void update(WaterQualitySubject subject);    /**
     * 設定觀察人員的職務
     * @param job 觀察人員的職務
     */
    public void setJob(String job);    /**
     * 擷取觀察人員的職務
     * @return 觀察人員的職務
     */
    public String getJob();
}/**
 * 具體的觀察者實作
 */public class Watcher implements WatcherObserver{
    /**
     * 職務
     */
    private String job;    public void update(WaterQualitySubject subject) {        //這裡采用的是拉的方式
        System.out.println(job+"擷取到通知,目前污染級别為:"+subject.getPolluteLevel());
    }    public String getJob() {        return this.job;
    }    public void setJob(String job) {        this.job = job;
    }
}public class Client {
    public static void main(String[] args) {        //建立水質主題對象
        WaterQuality subject = new WaterQuality();        //建立幾個觀察者
        WatcherObserver watcher1 = new Watcher();
        watcher1.setJob("監測人員");
        WatcherObserver watcher2 = new Watcher();
        watcher2.setJob("預警人員");
        WatcherObserver watcher3 = new Watcher();
        watcher3.setJob("監測部門上司");        //注冊觀察者
        subject.attach(watcher1);
        subject.attach(watcher2);
        subject.attach(watcher3);        //填寫水質報告
        System.out.println("當水質為正常的時候------------------〉");
        subject.setPolluteLevel(0);
        System.out.println("當水質為輕度污染的時候---------------〉");
        subject.setPolluteLevel(1);
        System.out.println("當水質為中度污染的時候---------------〉");
        subject.setPolluteLevel(2);
    }
}           

複制