1. 程式人生 > >設計模式學習筆記(3):觀察者模式(2)

設計模式學習筆記(3):觀察者模式(2)

       上一篇的觀察者模式的筆記中提到的例子,只是Subject把自身狀態“推”給Observer的方式。
    //通知觀察者資訊發生改變了
    @Override
    public void notifyObservers() {
        for (int i = 0;i < observers.size();i++){
            Observer observer = (Observer) observers.get(i);
            observer.update(travelerNum,terminal,information);
        }
    }

    //更新旅行資訊
    public void setTravelInformation(int travelerNum,String terminal,String information){
        this.travelerNum = travelerNum;
        this.information = information;
        this.terminal = terminal;
        notifyObservers();
    }

      從程式碼中可以看出,只要Subject發生改變,Observer就會被動接受全部資訊,並且不得不接受。

      這種模式的優勢在於:Observer不用呼叫多次,才收集全所有需要的訊息。

      同時這種模式會造成一定的麻煩:

      1、Observer可能並不需要所有的訊息;

      2、在Subject需要擴充套件時,需要更新每一個傳送給Observer的訊息  。

     這時我們可以在Subject中提供一些公開的getter方法,讓Observer自己索取所需要的資訊。

     在這裡我們還是選取旅行者的例子。

     Subject/Observable介面:

public interface Observable {
    public void registerObserver(Observer o);
    public void removeObserver(Observer o);
    public void notifyObservers();
}
    Observer介面:
public interface PrepareTravel {
    void doPrepare();
}

    用來展示的介面PrepareTravel():

public interface PrepareTravel {
    void doPrepare();
}

    Organizer去實現Obserable介面:

public class Organizer implements Observable {
    //標記Subject狀態是否發生改變
    private boolean changed;
    //旅行者人數
    private int travelerNum;
    //目的地
    private String terminal;
    //相關資訊
    private String information;
    //用於存放observers的陣列
    private ArrayList observers;

    //構造器
    public Organizer(){
        //初始化observers,否則會報空指標
        observers = new ArrayList();
        //標記預設為false,未發生改變
        changed = false;
    }

    //註冊observer
    @Override
    public void registerObserver(Observer o) {
        observers.add(o);
    }

    //刪除observer
    @Override
    public void removeObserver(Observer o) {
        int i = observers.indexOf(o);
        if (i >= 0){
            observers.remove(i);
        }
    }

    //通知observer
    @Override
    public void notifyObservers() {
        if (changed){
            for (int i = 0;i < observers.size();i++){
                Observer observer = (Observer) observers.get(i);
                observer.update(this);
            }
            changed = false;
        }
    }

    //改變發生
    public void setInformationChanged(int travelerNum,String terminal,String information){
        changed = true;
        this.travelerNum = travelerNum;
        this.terminal = terminal;
        this.information = information;
        notifyObservers();
    }

    //公共的getter方法供observer去“拉”所需要的資訊
    public int getTravelerNum() {
        return travelerNum;
    }

    public String getTerminal() {
        return terminal;
    }

    public String getInformation() {
        return information;
    }
}

在這裡我們引進了changed標記,使得更新觀察者時,擁有更多的彈性。在這個場景中,我們並不想每加一個人都通知觀察者Traveler,我們想變更達到10人才通知觀察者Traveler。我們可以新增條件:

    //改變發生
    public void setInformationChanged(int travelerNum,String terminal,String information){
        if (travelerNum - this.travelerNum >= 10){
            changed = true;
            this.travelerNum = travelerNum;
            this.terminal = terminal;
            this.information = information;
            notifyObservers();
        }
    }

Traveler去實現Observer介面:

public class Traveler implements Observer,PrepareTravel {
    //Subject
    Observable observable;
    //旅客人數
    private int travelerNum;
    //目的地
    private String terminal;
    //相關資訊
    private String information;

    //構造器
    public Traveler(Observable observable){
        this.observable = observable;
        //註冊
        observable.registerObserver(this);
    }

    //更新自己所需要的資訊
    @Override
    public void update(Observable o) {
        if (o instanceof Organizer){
            Organizer organizer = (Organizer) o;
            //可以“拉”自身所需要的資訊
            this.travelerNum = organizer.getTravelerNum();
            this.terminal = organizer.getTerminal();
            this.information = organizer.getInformation();
            doPrepare();
        }
    }

    //展示
    @Override
    public void doPrepare() {
        System.out.println("我已知悉這次旅行有"+travelerNum+"人,前往"+terminal+",其他相關資訊有"+information);
    }
}

模擬旅行:

public class TravelSimulator {
    public static void main(String[] args) {
        Organizer organizer = new Organizer();
        Traveler traveler = new Traveler(organizer);

        organizer.setInformationChanged(10,"九華山","安徽");
        organizer.setInformationChanged(11,"黃山","安徽");
        organizer.setInformationChanged(30,"峨眉山","四川");
    }
}

新增changed條件前結果:

新增changed條件後結果:

《HeadFirst》中,推的方式被認為更正確(請教了大佬):
有些實現中,會需要將改變前和改變後的值都放在通知裡,這就沒法用get了。