1. 程式人生 > >記一次觀察者模式的使用

記一次觀察者模式的使用

一、引入:

今天做播放器時碰到了一個小問題:
有三個地方需要同一組資料,而且分屬不同地方,如何同步?
當然有很多方法可以實現,本文主要講觀察者模式,也算是回虐它吧(曾經被它吊打...)
注意,本文使用的是測試程式碼,僅是模擬情況(Android上的使用道理是一致的,已實證)

資料同步


把問題簡化為下面7個類:
需求:資料在SongSubject中的改變,可以通知三個觀察者,並同時更新資料

問題抽離


二、觀察者模式:

一對多--一人提供資訊(Subject),多人需求資訊(Observer),資訊體(T)
T發生改變時,由Subject統一提醒Observer


1.介面層:Observer介面:(觀察者)
public interface Observer<T> {
    /**
     * 更新
     * @param t 觀察變化的資訊體
     */
    void update(T t);
}
複製程式碼

2.介面層:Subject介面:(被觀察者)
public interface Subject<T> {
    /**
     * 觀察者關聯
     * @param o 待觀察者(資訊體)
     */
    void attach(Observer<T> o);

    /**
     * 觀察者取消關聯
     * @param o 待觀察者()
     */
    void detach(Observer<T> o);

    /**
     * 當資訊體更新時呼叫,用於通知觀察者
     */
    void notifyObserver();
}
複製程式碼

3.資訊體(Song)
public class Song {
    private String title;
    private String singer;
    private long seek;

    public Song(String title, String singer, long seek) {
        this.title = title;
        this.singer = singer;
        this.seek = seek;
    }
    //set、get、toString省略...
}

複製程式碼

4.資訊提供者(被觀察者實現類):
public class SongSubject implements Subject<Song>{
    private List<Observer<Song>> mObservers = new ArrayList<>();//觀察者物件集合
    private Song mSong;

    @Override
    public void attach(Observer<Song> observer) {
        mObservers.add(observer);
    }

    @Override
    public void detach(Observer<Song> observer) {
        mObservers.remove(observer);
    }

    @Override
    public void notifyObserver() {
        for (Observer<Song> o : mObservers) {
            o.update(mSong);
        }
    }

    /**
     * 設定資訊--通知所有觀察者
     * @param song
     */
    public void setSong(Song song) {
        mSong = song;
        this.notifyObserver();
    }
}
複製程式碼

5.Pop觀察者實現類
/**
 * 作者:張風捷特烈
 * 時間:2018/12/24 0024:21:27
 * 郵箱:[email protected]
 * 說明:觀察者:Pop
 */
public class PopPager implements Observer<Song> {

    @Override
    public void update(Song song) {
        //TODO:更新時的檢視渲染
        System.out.println("PopPager:" + song);
    }

}
複製程式碼

6.Fragment觀察者實現類
/**
 * 作者:張風捷特烈
 * 時間:2018/12/24 0024:21:27
 * 郵箱:[email protected]
 * 說明:觀察者:Fragment
 */
public class HomeListFragment implements Observer<Song> {
    @Override
    public void update(Song song) {
        //TODO:更新時的檢視渲染
        System.out.println("HomeListFragment:" + song);
    }
}

複製程式碼

7.主頁面觀察者實現類

主要繫結邏輯在模仿的onCreate裡,當然mpv裡,你可以根據實際情況

/**
 * 作者:張風捷特烈
 * 時間:2018/12/24 0024:21:27
 * 郵箱:[email protected]
 * 說明:觀察者:Activity
 */
public class HomeActivity implements Observer<Song> {
    private int seek;

    public void onCreate() {
    
        SongSubject songSubject = new SongSubject();
        PopPager popPager = new PopPager();//常見Pop
        HomeListFragment homeListFragment = new HomeListFragment();//建立Fragment
        songSubject.attach(this);//HomeActivity觀察資訊
        songSubject.attach(homeListFragment);//homeListFragment觀察資訊
        songSubject.attach(popPager);//popPager觀察資訊

        Song song = new Song("勇氣", "葛強麗", seek);//資訊模擬
        Timer timer = new Timer();//計時器輪訓任務
        timer.schedule(new TimerTask() {
            public void run() {
                song.setSeek(seek++);//修改資訊
                songSubject.setSong(song);//設定資訊,通知所有觀察者
            }
        }, 0, 1000);
    }

    @Override
    public void update(Song song) {
        //TODO:更新時的檢視渲染
        System.out.println("HomeActivity:" + song);
    }
}
複製程式碼

8.啟動類:
public class Boot {
    public static void main(String[] args) {
        HomeActivity activity = new HomeActivity();
        activity.onCreate();
    }
}
複製程式碼

三、觀察者模式分析

1.執行結果如下圖

結果.gif


2.分析:

單看結果好像並沒有什麼特色,但是請注意:

每次的印的三條資料分別來源於三個不同的類
當你在update方法裡用song物件控制檢視顯示時,只需要讓被觀察者更新資料就行了
三個介面的資訊會同步變化,這就是觀察者模式優秀的地方
畢竟實際中Pop彈框,Activity,Fragement分屬不同類,能夠這樣統一變化會減少耦合
複製程式碼

3.小結

設計模式還是在需要的時候能理解的清晰一些,乾巴巴的看典例總感覺也就那樣
最近在做個人播放器玩一下,整個體系挺大的,也比較碎,應該不能成文了,等完善發後原始碼吧