設計模式之觀察者模式(Obsever)
(1)觀察者模式定義了一種一對多的依賴關係,讓多個觀察者物件同時監聽某一個主題物件。這個主題物件在狀態上發生變化時,會通知所有觀察者物件,讓他們能夠自動更新自己。
(2)觀察者模式的組成
抽象主題角色:把所有對觀察者物件的引用儲存在一個集合中,每個抽象主題角色都可以有任意數量的觀察者。抽象主題提供一個藉口,可以增加和刪除觀察者角色。一般用一個抽象類或介面來實現。
抽象觀察者角色:為所有具體的觀察者定義一個介面,在得到主題的通知時更新自己。
具體主題角色:在具體主題內部狀態改變時,給所有登記過的觀察者發出通知。具體主題角色通常用一個子類實現。
具體觀察者角色:該角色實現抽象觀察者角色所要求的更新介面,以便使本身的狀態與主題的狀態相協調。如果需要,具體觀察者角色可以儲存一個指向具體主題角色的引用。通常用一個子類實現。
(3)下面是一個觀察者模式的例子
抽象主題角色:
package com.sailang.obsever;
public interface Watched
{
public void addWatcher(Watcher watcher);
public void removeWatcher(Watcher watcher);
public void notifyWatchers(String str);
}
抽象觀察者角色:
package com.sailang.obsever; public interface Watcher { public void upDate(String str); }
具體主題角色:
package com.sailang.obsever; import java.util.ArrayList; import java.util.List; public class ConcreteWatched implements Watched { private List<Watcher> list = new ArrayList<Watcher>(); @Override public void addWatcher(Watcher watcher) { list.add(watcher); } @Override public void removeWatcher(Watcher watcher) { list.remove(watcher); } @Override public void notifyWatchers(String str) { for(Watcher watcher : list) { watcher.upDate(str); } } }
具體觀察者角色:
package com.sailang.obsever;
public class ConcreteWatcher implements Watcher
{
@Override
public void upDate(String str)
{
System.out.println(str);
}
}
客戶端:
package com.sailang.obsever;
public class Test
{
public static void main(String[] args)
{
Watched watched = new ConcreteWatched();
Watcher watcher1 = new ConcreteWatcher();
Watcher watcher2 = new ConcreteWatcher();
Watcher watcher3 = new ConcreteWatcher();
//將所有的觀察物件在主題物件中進行註冊
watched.addWatcher(watcher1);
watched.addWatcher(watcher2);
watched.addWatcher(watcher3);
//主題物件狀態改變
watched.notifyWatchers("Hello");
}
}
(4)在AWT中事件模型所採用的就是觀察者模式,如下面的例子:
package com.sailang.obsever2;
import java.awt.Button;
import java.awt.Frame;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class Test
{
public static void main(String[] args)
{
Frame frame = new Frame();
Button button = new Button("點我");
button.addActionListener(new ButtonListener());
frame.add(button);
frame.pack();
frame.setVisible(true);
}
}
class ButtonListener implements ActionListener
{
@Override
public void actionPerformed(ActionEvent e)
{
String label = e.getActionCommand();
System.out.println(label);
}
}
Button就是具體主題角色,ActionListener是抽象觀察者角色,ButtonListener是具體觀察者角色。
actionPerformed()就是抽象觀察角色為所有具體觀察者角色定義的一個介面,用於得到主題角色的通知來更新自己。
button.addActionListener()就是把一個觀察物件加到自己的List中。
而狀態的更新就是“點選按鈕”,這個不用開發者來操心,內部機制會幫我們實現,從外表來看就好像是自動呼叫似的。
(5)編寫一個程式,宣告一個類,該類繼承自Observable(因此該類是個主題角色),有一個int型別的變數,初始值為10,編寫一個for迴圈,將該數字每次遞減1,一直到0為止,每次變化時,都將該數字傳遞給它的觀察者,觀察者會打印出該數字;第二個觀察者在該數字為5之後開始列印該數字。
主題物件(即被觀察物件)
package com.sailang.obsever2;
import java.util.Observable;
public class BeingWatched extends Observable
{
public void counter()
{
for(int i = 10; i >= 0; i--)
{
setChanged();
notifyObservers(i);
}
}
}
觀察物件1
package com.sailang.obsever2;
import java.util.Observable;
import java.util.Observer;
public class Watcher implements Observer
{
@Override
public void update(Observable o, Object arg)
{
System.out.println(arg);
}
}
觀察物件2
package com.sailang.obsever2;
import java.util.Observable;
import java.util.Observer;
public class Watcher2 implements Observer
{
@Override
public void update(Observable o, Object arg)
{
if((Integer)arg > 5)
return;
System.out.println(arg);
}
}
客戶端:
package com.sailang.obsever2;
public class Test2
{
public static void main(String[] args)
{
BeingWatched beingWatched = new BeingWatched();
Watcher watcher = new Watcher();
Watcher2 watcher2 = new Watcher2();
beingWatched.addObserver(watcher);
beingWatched.addObserver(watcher2);
//狀態改變
beingWatched.counter();
}
}