觀察者模式(Observer Pattern)

觀察者模式(Observer Pattern)

有時候不知道你會不會出現一些疑問?
當你為一些元件加入事件以後, 而這些元件產生變化的時候,
為什麼系統會知道是哪一個元件產生變化的呢?
其實他是由一種設計模式產生的, 這種設計模式稱作為”觀察者模式”

什麼叫做觀察者模式,
觀察者模式由三個角色所構成, 分別是觀察者、觀察者物件、主題物件,
假設有4個觀察者物件,其中3個觀察者物件加入了觀察者名單,
每當主題物件發布了某些訊息,
有加入觀察者名單的觀察者物件就會透過觀察者收到該訊息,
沒有加入觀察者名單的觀察者物件當然就不會收到訊息,
而如果已經在觀察者名單內的物件或者不在觀察者名單內的物件,
仍然可以自由的加入觀察者名單或者從觀察者名單內移除。

呵呵,有點頭昏腦脹了嗎?舉個白話點的例子,
現在假設大家都有玩過facebook的經驗,
而facebook有一種模式叫做粉絲團,
當你加入某一個粉絲團的時候, 當該粉絲團發布某項訊息時,
該粉絲團的粉絲都會收到這項訊息,
而當你退出這個粉絲團的時候, 就在也收不到該粉絲團的訊息了!
這樣就稱作一個觀察者模式。

現在把角色換成Button物件、Listener和Event物件,
Event物件就像是粉絲, Button物件的角色就像粉絲團,
當你把某一個Event物件加入了Button物件的Listener名單,
當button物件產生變化的時候, Listener就會去通知加入的Event物件。

button1.setOnClickListener(new OnClickListener(){
    public void onClick(View v){
        textView1.setText("textview1");
    }
});

button1就是主題物件
setOnClickListener的行為就是加入觀察名單
new OnClickListener() 就是觀察者物件

當button產生變化的時候,
觀察者就去觀察者名單內查看有哪些觀察者物件加入,
然後逐一的通知它們。

我們來模擬一下Button按下去的情節,
這個程式用來模擬Android的Button按下的時候, 要處理什麼樣的動作,
首先你還是一樣要先宣告一個事件,然後把這個事件加入button的監聽器,

button1.setOnClickListener(new OnClickListener(){
    public void onClick(View v){
        textView1.setText("textview1");
    }
});

可是我們模擬的button並不是android內建的,
如果要讓android顯示出來這兩個button,
還要在寫一些button的描述, 這樣太複雜了,
因此我們就真的宣告android內建的button,
當button按下的時候, 就去呼叫我們做出來的”假Button”裡面的”onclick”,
以達到模擬的效果。

而我們的重點放在模擬
android的button到底是怎麼實作出setOnClickListener()這個方法,

想像一下setOnClickListener是被Button的實體物件呼叫,
因此, 我們可以猜到上層Button這個類別一定有一個方法叫做setOnClickLister(),
並且會傳入OnClickListener這個物件的方法,
所以我們作出一個假button的類別,

interface FButton{
    public void setOnClickListener(FakeOnLister listener);
}

這樣一來, 我們就可以知道有一個類別會繼承這個介面,
然後實作setOnClickListener這個方法。

在來我們想一下既然有傳入OnClickListener這個物件,
而且會實作onClick()這個方法
那麼我們也來定義一個假的Listener

interface FakeOnLister {
    void onClick(int index);
}

由前面知道觀察者模式, 因此我們的Button裡面會出現幾個必要的方法,
首先是加入觀察者物件,移除觀察者物件,以及通知所有觀察者,
所以類別可以這樣寫出來,

class FakeButton implements FButton{
    private List<FakeOnLister> listenerList;
    public FakeButton(){
        listenerList = new ArrayList<FakeOnLister>();
    }
    public void setOnClickListener(FakeOnLister listener) {
        listenerList.add(listener);
    }
    public void removeOnClickListener(FakeOnLister listener){
        int index = listenerList.indexOf(listener);
        if(index>=0){
            listenerList.remove(index);
    }
    public void onClick() {
        for (int i=0; i<listenerList.size(); i++) {
            listenerList.get(i).onClick(i);
        }
    }
}

加入觀察者物件

public void setOnClickListener(FakeOnLister listener)

移除觀察者物件

public void removeOnClickListener(FakeOnLister listener)

通知所有觀察者

public void onClick()

所以可以開始使用這個”假Button”了

private FakeButton fakeButton1;
private FakeButton fakeButton2;

public void onCreate(Bundle savedInstanceState) {
    fakeButton1 = new FakeButton();
    fakeButton2 = new FakeButton();
    fakeButton1.setOnClickListener(new FakeOnLister(){

        @Override
        public void onClick(int index) {
            // TODO Auto-generated method stub
            Toast.makeText(getApplicationContext(), 
                "button1 click",
                Toast.LENGTH_SHORT).show();
        }
    });
    fakeButton2.setOnClickListener(new FakeOnLister(){

        @Override
        public void onClick(int index) {
           // TODO Auto-generated method stub
           Toast.makeText(getApplicationContext(), 
               "button2 click",
               Toast.LENGTH_SHORT).show();
        }

    });
}

當我們按下假button 1號就會印出Log訊息顯示我們按下的是假button 1號
同理, 假button 2號。

接著我們重複剛剛所說的用真的button去模擬我們寫出來的假button,
借用一下他們的onClick動作來證實我們的button是可以動的。

button1 = (Button)findViewById(R.id.a_button);
button2 = (Button)findViewById(R.id.b_button);
button1.setOnClickListener(new OnClickListener(){

    @Override
    public void onClick(View arg0) {
        // TODO Auto-generated method stub
        fakeButton1.onClick();
    } 
});
button2.setOnClickListener(new OnClickListener(){

    @Override
    public void onClick(View arg0) {
        // TODO Auto-generated method stub
        fakeButton2.onClick();
    }

});

如此一來, 我們可以在畫面看到兩個button,
當按下去的時候, 的確可以跑到我們一開始所期望的情況。