Java設計模式(6)之觀察者模式學習總結

    觀察者模式:在觀察者模式中,存在着對象之間的一對多的依賴關係,即一個對象的狀態發生改變時,所有依賴於該對象的對象都會得到通知,並對自身的狀態進行更新;
    觀察者模式的學習中,對象之間的一對多的依賴關係是學習觀察者模式的切入點,而被依賴對象(目標對象)的狀態改變會對依賴對象(觀察者對象)狀態產生影響是觀察者模式的關鍵所在;只有對象之間形成一對多的依賴關係,才能實現被依賴對象與依賴對象之間的狀態更新的交互作用;
    生活中,其實存在很多觀察者模式的應用場景,比如天氣預報短信通知,當你在手機上開通了短信通知天氣服務之後,每天你都會收到當天的天氣信息的短信通知;再比如,你的手機上安裝了騰訊體育APP,那麼你只要接受該APP提供的推送即時消息通知的服務,那麼一旦有更新的體育信息,將會向你的手機推送相關信息;
    下面通過一個生活中的場景,進行相關的觀察者模式設計代碼:
    package com.pattern.observer;
    import java.util.ArrayList;
    import java.util.List;
    /**
     * 抽象目標類
     * @author Administrator
     */
    public  class Subject {
        //訂閱者列表,用於存放訂閱者對象
        private List<Observer> list = new ArrayList<Observer>();
        //添加訂閱者
        public void add(Observer o) 
        {
            list.add(o);
        }
        //刪除指定訂閱者
        public void delete(Observer o)
        {
            list.remove(o);
        }
        //通知所有訂閱者
        public void notifyAllObservers() 
        {
            for (Observer observer : list)
             {
                //將目標對象傳入觀察者對象的update方法
                observer.update(this);
            }
        }
    }


    package com.pattern.observer;
    /**
     * 具體目標類
     * @author Administrator
     */
    public class WeatherSubject extends Subject {
        //目標對象的狀態信息
        private String information;
        public String getInformation() 
        {
            return information;
        }
        //設置或更新目標狀態信息
        public void setInformation(String information)
         {
            this.information = information;
            //更新狀態信息後,調用通知方法,通知所有訂閱者,使其進行各自的狀態更新
            this.notifyAllObservers();
        }   
    }


    package com.pattern.observer;
    /**
     * 觀察者接口
     * @author Administrator
     */
    public interface Observer {
        //更新觀察者狀態
        public abstract void update(Subject subject);
    }


    package com.pattern.observer;
    /**
     * 具體觀察者類
     * @author Administrator
     */
    public class ConcreteObserver implements Observer {
        //觀察者的名字
        private String observerName;
        //觀察者的待更新狀態信息
        private String information;
        //接收到信息後的響應信息
        private String hint;

        public ConcreteObserver(String name,String hint)
         {
            observerName = name;
            this.hint = hint;
        }

        @Override
        public void update(Subject subject) 
            information =((WeatherSubject)subject).getInformation();
            System.out.println(observerName+"收到了"+information+"的通知"+","+hint);
        }

    }


    package com.pattern.observer;
    //測試類
    public class TestObserverPattern {

        public static void main(String[] args) {
            //1.創建目標對象
            WeatherSubject subject = new WeatherSubject(); 
            //2.創建觀察者對象
            ConcreteObserver observerOne = new ConcreteObserver("Tom", "已收到,考慮是否去購物");
            ConcreteObserver observerTwo = new ConcreteObserver("Jake", "已收到,考慮是否去旅遊");
            //3.添加觀察者
            subject.add(observerOne);
            subject.add(observerTwo);
            //4.更新目標狀態信息
            subject.setInformation("天氣晴朗");
        }
    }
    在設計觀察者模式的時候,由於對於向觀察者對象傳送的消息內容的不同,觀察者模式又分爲推模型和拉模型;

    拉模型:將目標對象傳給觀察者對象,觀察者對象需要目標對象的哪一部分的狀態信息,就通過該目標對象獲取信息;當觀察者對象對更新信息的需求發生變化時,拉模型可以很好地應對變化的場景;在上述的例子中,採用的是拉模型進行觀察者模型的設計; 

    推模型:將指定的更新信息通知給觀察者對象,當觀察者對更新信息有了不同的要求之後,這種方式將會顯得難以適應變化的要求;

    如果想將上述例子通過推模型實現,只需要對具體觀察者類中的update進行修改和對具體目標類中的notifyAllObservers進行重寫;修改如下: 

    public void notifyAllObservers() 
    {
        for (Observer observer : list)
         {
            //將傳入觀察者對象的update方法
            observer.update(information);
        }
    }

    public void update(String information) 
    {
        this.information = information;
        System.out.println(observerName+"收到了"+information+"的通知"+","+hint);
    }

    在Java中,其實提供了觀察者模式的實現,利用java.util包下的Observable類和Observer接口就可以大大簡化我們自己設計觀察者模式的步驟;利用Java提供的觀察者模式的實現將上述例子重新進行設計:
    /**
     *目標類的設計
     */
    package com.pattern.observerInJava;
    import java.util.Observable;

    public class ConcreteSubject extends Observable  
    {
        private String weatherInfor;//天氣信息
        public String getWeatherInfor() 
        {
            return weatherInfor;
        }
        public void setWeatherInfor(String weatherInfor)  
        {
            this.weatherInfor = weatherInfor;
            //使用Java提供的觀察者模式,在進行通知觀察者更新信息之前所必需的一步
            this.setChanged();
            //設置拉模型,將目標對象直接傳到觀察者方法中
            this.notifyObservers();
            //設置推模型,將具體需要通知的信息傳到觀察着方法中
            this.notifyObservers(weatherInfor);
        }   
    }


    /**
     *觀察者類的設計
     */
    package com.pattern.observerInJava;
    import java.util.Observable;
    import java.util.Observer;

    public class ConcreteObserver1 implements Observer 
    {
        private String observerName;
        private String weatherInfor;

        public ConcreteObserver1(String name) 
        {
            observerName = name;
        }
        @Override
        public void update(Observable o, Object arg)  
        {
            //採用拉模型進行觀察者消息的更新
            weatherInfor = ((ConcreteSubject)o).getWeatherInfor();
            System.out.println(observerName+"從目標處拉取了更新的信息");
            System.out.println(observerName+"收到了"+weatherInfor);
            //採用推模型進行觀察者消息的更新
            System.out.println(observerName+"接收到了推動的更新的信息");
            System.out.println(observerName+"收到了"+weatherInfor);
        }
    }


    /**
     *測試類的設計
     */
    package com.pattern.observerInJava;
    import com.pattern.observerInJava.ConcreteObserver1;
    public class TestObserverInJava
    {
        public static void main(String[] args)  
        {
            //1.創建目標對象
            ConcreteSubject subject = new ConcreteSubject(); 
            //2.創建觀察者對象
            ConcreteObserver1 observerOne = new ConcreteObserver1("Tom");
            ConcreteObserver1 observerTwo = new ConcreteObserver1("Jake");
            //3.添加觀察者
            subject.addObserver(observerOne);
            subject.addObserver(observerTwo);
            //4.更新目標狀態信息
            subject.setWeatherInfor("天氣晴朗");
        }
    }
    進一步地提出問題,當不同的觀察者對象對目標對象的更新信息中感興趣的方面不同,比如觀察者對象A只對下雨天和陰天感興趣,觀察者對象B只對下雨天感興趣,這時,將需要根據不同的需求向不同的觀察者對象發送相應的更新信息;
    這裏可以使用面向接口編程的思想進行解決,設計一個抽象目標類,在該類中實現其具體實現類中公有的部分,而將notifyAllObservers方法放在其具體實現類中進行實現,在該方法中根據需求的不同進行不同的處理;
    notifyAllObservers方法的具體實現可參考如下代碼:
    public void notifyAllObservers() 
    {
        for (Observer observer : list)
         {
             //如果天氣爲下雨天,則只向觀察者A、B推送更新信息
            if("下雨天".equals(weatherInfor))
            {
                if(observer.getObserverName.equals("A"))
                {
                    observer.update(this);
                }
                if(observer.getObserverName.equals("B"))
                {
                    observer.update(this);
                }
            }
            //如果天氣爲陰天,則指向觀察者A推送更新信息
            if("陰天".equals(weatherInfor))
            {
                if(observer.getObserverName.equals("A"))
                {
                    observer.update(this);
                }
            }
        }
    }


    在當自己進行觀察者模式設計的時候,這裏給出一些適用於觀察者模式設計的注意點:
        ①命名上的建議:
            目標接口定義爲Subject
            觀察者接口定義爲Observer
            觀察者接口中的的更新方法定義爲update
        ②觸發通知的時機應該在更新完目標對象的狀態信息之後通知
        ③通知的順序:觀察者之間是平行的,在觀察者模式中不需要指定通知的順序 
    觀察者模式設計流程分析:
        準備階段 1、創建目標對象 2、創建觀察者對象 3、在目標對象中註冊觀察者對象
        運行階段 1、改變目標對象的狀態 2、通知所有已註冊觀察者對象進行相應的處理 3、回調目標對象,獲取相應數據

    觀察者優點及適用場景:
    優點:
    ①觀察者模式實現了觀察者和目標之間的抽象耦合;
    ②觀察者模式實現了動態聯動;
    ③觀察者模式支持廣播通信;
    使用場景:
    ①在一個業務場景中,某一方面的操作需要依賴另一方面狀態的改變;
    ②在更改一個對象的狀態同時,需要連帶着修改其他對象的狀態,並且需要被連帶修改的對象的個數不知道;
    ③當需要實現通知對象和被通知對象之間的鬆散耦合的時候,選用觀察者模式;
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章