有時被稱做發佈/訂閱模式,觀察者模式定義了一種一對多的依賴關係,讓多個觀察者對象同時監聽某一個主題對象。這個主題對象在狀態發生變化時,會通知全部觀察者對象,使它們可以自動更新本身。它相似B/S架構模式,構建一個服務端,多個客戶端顯示。其實這個主題對象就像是一個信息源,當信息源的狀態發送變化時,它會通知全部訂閱者,使它們進行相應的處理。在百度百科中的例子是,用戶界面能夠做爲一個觀察者,業務數據是被觀察者,用戶界面觀察業務數據的變化,發現數據變化後,就顯示在界面上。html
注意:java
觀察者的信息是來源於主題中notify方法所傳遞信息,好比notify(String str)傳遞字符str信息,觀察者update(String strObj)就會接收str信息,並進行相關操做。架構
1.當一個抽象模型有兩個方面, 其中一個方面依賴於另外一方面。將這兩者封裝在獨立的對象中以使它們能夠各自獨立地改變和複用。ide
2.當對一個對象的改變須要同時改變其它對象, 而不知道具體有多少對象有待改變。測試
3.當一個對象必須通知其它對象,而它又不能假定其它對象是誰。換言之, 你不但願這些對象是緊密耦合的。this
公司開會,領導先通知各部門的負責人去會議室(註冊、登記過程),在會議室中,你們都在等待領導的講話(多個觀察者關注同一個主題對象),領導開始說道,「今年的時間已過半了,但是業績尚未完成預計的40%啊,你們可得努力」(通知過程),你們聽到領導這番話時,市場部經理開始說話,「因爲上半年的整個市場比較萎靡,所以市場拓展方面比較緩慢,可是通過上半年的調查,咱們掌握了比較重要的信息,下半年,咱們市場部確定會拓展更多的份額」;研發部經理,「咱們研發部會繼續加班加點,保證完成任務」(各個觀察者接到通知後的處理過程)。spa
ILeader:抽象主題(Subject).net
public interface ILeader
{
// 註冊登記
public void addManager(IManager manager);
// 移除已註冊的
public void removeManager(IManager manager);
// 通知全部經理
public void notifyManagers(String str);
}
MyLeader:具體主題(ConcreteSubject)設計
public class MyLeader implements ILeader
{
// 存儲全部需註冊登記的經理
private List<IManager> managerList = new ArrayList<IManager>();
@Override
public void addManager(IManager manager)
{
synchronized (this)
{
if (manager != null && !(managerList.contains(manager)))
{
managerList.add(manager);
}
}
}
@Override
public void removeManager(IManager manager)
{
synchronized (this)
{
if (managerList.contains(manager))
{
managerList.remove(manager);
}
}
}
@Override
public void notifyManagers(String str)
{
for (IManager iManager : managerList)
{
iManager.update(str);
}
}
}
IManager:抽象觀察者(Observer)server
public interface IManager
{
/**
* 更新
* @param str 與ILeader的通知參數一致
*/
public void update(String str);
}
MarketingManager:具體觀察者(ConcreteObserver)
public class MarketingManager implements IManager
{
@Override
public void update(String str)
{
System.out.print("市場部接收到命令: ");
doSomething(str);
}
private void doSomething(String str)
{
if (str.equals("努力"))
{
System.out.println("下半年,咱們市場部確定會拓展更多的份額");
}
}
}
DevelopManager:具體觀察者(ConcreteObserver)
public class DevelopManager implements IManager
{
@Override
public void update(String str)
{
System.out.print("研發部接收到命令: ");
doSomething(str);
}
private void doSomething(String str)
{
System.out.println("咱們研發部會繼續加班加點,保證完成任務");
}
}
測試類:
public class TestMain
{
public static void main(String[] args)
{
ILeader leader = new MyLeader();
IManager marketManager = new MarketingManager(); // 市場部
IManager developManager = new DevelopManager(); // 研發部
// 註冊,登記過程
// 方式一
leader.addManager(marketManager);
leader.addManager(developManager);
// 方式二 匿名:以工程部爲例
leader.addManager(new IManager() // 工程部
{
@Override
public void update(String str)
{
System.out.print("工程部接收到命令: ");
if (str.equals("努力"))
{
doSomething();
}
}
private void doSomething()
{
System.out.println("咱們工程部會加快工程的實施進度");
}
});
System.out.println("領導講話:先談談上半年的業績!");
// 通知過程
System.out.println("發送努力命令");
leader.notifyManagers("努力");
}
}
輸出結果:
領導講話:先談談上半年的業績!
發送努力命令
市場部接收到命令: 下半年,咱們市場部確定會拓展更多的份額
研發部接收到命令: 咱們研發部會繼續加班加點,保證完成任務
工程部接收到命令: 咱們工程部會加快工程的實施進度
在實際中,爲了方便性,咱們更多使用的是第二種註冊方式。
觀察者模式在關於目標(主題)角色、觀察者角色通訊的具體實現中,有兩個版本。
1) 拉模式:目標角色在發生變化後,僅僅告訴觀察者角色「我變化了」;觀察者角色若是想要知道具體的變化細節,則就要本身從目標角色的接口中獲得。拉模式是想要就主動表白獲取。
2) 推模式:通知你發生變化的同時,經過一個參數將變化的細節傳遞到觀察者角色中去。推模式是管你要不要,先給你啦。
這兩種模式的使用,取決於系統設計時的須要。若是目標角色比較複雜,而且觀察者角色進行更新時必須獲得一些具體變化的信息,則「推模式」比較合適。若是目標角色比較簡單,則「拉模式」就很合適啦。
當事件源對象上發生操做時,它將會調用事件監聽器的一個方法,並在調用該方法時傳遞事件對象過去。
Java中的事件監聽機制主要由事件源、事件對象、事件監聽器三個部分組成。
1)事件源(event source):
具體的事件源,好比說,你點擊一個button,那麼button就是event source,要想使button對某些事件進行響應,你就須要註冊特定的listener。
2)事件對象(event object):
通常繼承自java.util.EventObject類,封裝了事件源對象以及與事件相關的信息。它是在事件源和事件監聽器之間傳遞信息的。
3)事件監聽器(event listener):
實現java.util.EventListener接口,須要註冊在事件源上才能被調用。它監聽事件,並進行事件處理或者轉發。
然而,事件監聽機制與觀察者模式的關係呢?
觀察者(Observer)至關於事件監聽者,被觀察者(Observable)或者說主題(Subject)至關於事件源和事件,執行邏輯時通知observer便可觸發oberver的update,同時可傳被觀察者和參數。
其實事件機制中的「事件對象」就至關於上例觀察者模式中的notify中的String參數對象。
DoorEvent:事件對象(event object)
public class DoorEvent extends EventObject
{
private String doorState = ""; // 表示門的狀態,有「開」和「關」兩種
public DoorEvent(Object source)
{
super(source);
}
public void setDoorState(String doorState)
{
this.doorState = doorState;
}
public String getDoorState()
{
return this.doorState;
}
}
IDoorListener:事件監聽器(event listener)
public interface IDoorListener extends EventListener
{
//EventListener是全部事件偵聽器接口必須擴展的標記接口、由於它是無內容的標記接口、
//因此事件處理方法由咱們本身聲明以下:
public void dealDoorEvent(DoorEvent event);
}
FrontDoorListener:事件監聽器(event listener)
public class FrontDoorListener implements IDoorListener
{
/**
* 作具體的開門,關門動做
* @param event
*/
@Override
public void dealDoorEvent(DoorEvent event)
{
if (event.getDoorState()!=null && event.getDoorState().equals("open"))
{
System.out.println("前門打開");
}
else
{
System.out.println("前門關閉");
}
}
}
DoorManager:事件源(event source)
public class DoorManager
{
private List<IDoorListener> listeners = new ArrayList();
public void addDoorListener(IDoorListener listener)
{
synchronized (this)
{
if (listener != null && !(listeners.contains(listener)))
{
listeners.add(listener);
}
}
}
public void removeDoorListener(IDoorListener listener)
{
synchronized (this)
{
if (listeners.contains(listener))
{
listeners.remove(listener);
}
}
}
public void notifyDoors(DoorEvent event)
{
for (IDoorListener iDoorListener : listeners)
{
iDoorListener.dealDoorEvent(event);
}
}
/**
* 模擬開門事件
*/
public void fireOpend()
{
if (listeners == null)
{
return;
}
DoorEvent event = new DoorEvent(this);
event.setDoorState("open");
notifyDoors(event);
}
}
測試類:
public class TestMain
{
public static void main(String[] args)
{
DoorManager doorManager = new DoorManager();
// 添加監聽器
doorManager.addDoorListener(new FrontDoorListener());
doorManager.addDoorListener(new IDoorListener()
{
@Override
public void dealDoorEvent(DoorEvent event)
{
if (event.getDoorState() != null && event.getDoorState().equals("open"))
{
System.out.println("後門打開,警示燈亮起");
}
else
{
System.out.println("後門關閉,警示燈熄滅");
}
}
});
// 模擬事件
System.out.println("模擬門打開事件");
doorManager.fireOpend();
System.out.println("模擬門關閉事件");
DoorEvent doorEvent = new DoorEvent(doorManager);
doorEvent.setDoorState("close");
doorManager.notifyDoors(doorEvent);
}
}
輸出結果:
模擬門打開事件
前門打開
後門打開,警示燈亮起
模擬門關閉事件
前門關閉
後門關閉,警示燈熄滅
另外一示例:
參考:
一、http://www.cnblogs.com/wangjq/archive/2012/07/12/2587966.html
二、http://www.cnblogs.com/abcdwxc/archive/2007/09/19/898856.html
三、http://blog.csdn.net/ai92/article/details/375691
四、http://enjiex.iteye.com/blog/1067650