觀察者模式與事件監聽機制

1、觀察者模式

1.1 概述

有時被稱做發佈/訂閱模式,觀察者模式定義了一種一對多的依賴關係,讓多個觀察者對象同時監聽某一個主題對象。這個主題對象在狀態發生變化時,會通知全部觀察者對象,使它們可以自動更新本身。它相似B/S架構模式,構建一個服務端,多個客戶端顯示。其實這個主題對象就像是一個信息源,當信息源的狀態發送變化時,它會通知全部訂閱者,使它們進行相應的處理。在百度百科中的例子是,用戶界面能夠做爲一個觀察者,業務數據是被觀察者,用戶界面觀察業務數據的變化,發現數據變化後,就顯示在界面上。html

1.2 模式中的參與者

  • 抽象主題(Subject):它把全部觀察者對象的引用保存到一個彙集裏,每一個主題均可以有任何數量的觀察者。抽象主題提供一個接口,能夠增長和刪除觀察者對象,以及通知全部觀察者。
  • 具體主題(ConcreteSubject):將有關狀態存入具體觀察者對象;當具體主題內部狀態放生改變時,通知全部註冊過的觀察者。
  • 抽象觀察者(Observer):爲全部的具體觀察者定義一個接口,在獲得主題通知時更新本身。
  • 具體觀察者(ConcreteObserver):實現抽象觀察者角色所要求的更新接口,以便使自己的狀態與主題狀態保持一致。

                                         

注意java

觀察者的信息是來源於主題中notify方法所傳遞信息,好比notify(String str)傳遞字符str信息,觀察者update(String strObj)就會接收str信息,並進行相關操做。架構

 

1.3 適用性

1.當一個抽象模型有兩個方面, 其中一個方面依賴於另外一方面。將這兩者封裝在獨立的對象中以使它們能夠各自獨立地改變和複用。ide

2.當對一個對象的改變須要同時改變其它對象, 而不知道具體有多少對象有待改變。測試

3.當一個對象必須通知其它對象,而它又不能假定其它對象是誰。換言之, 你不但願這些對象是緊密耦合的。this

1.4 舉例說明

公司開會,領導先通知各部門的負責人去會議室(註冊、登記過程),在會議室中,你們都在等待領導的講話(多個觀察者關注同一個主題對象),領導開始說道,「今年的時間已過半了,但是業績尚未完成預計的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.5 我推你拉

觀察者模式在關於目標(主題)角色、觀察者角色通訊的具體實現中,有兩個版本。

1) 拉模式:目標角色在發生變化後,僅僅告訴觀察者角色「我變化了」;觀察者角色若是想要知道具體的變化細節,則就要本身從目標角色的接口中獲得。拉模式是想要就主動表白獲取。

2) 推模式:通知你發生變化的同時,經過一個參數將變化的細節傳遞到觀察者角色中去。推模式是管你要不要,先給你啦。

這兩種模式的使用,取決於系統設計時的須要。若是目標角色比較複雜,而且觀察者角色進行更新時必須獲得一些具體變化的信息,則「推模式」比較合適。若是目標角色比較簡單,則「拉模式」就很合適啦。

2、事件監聽機制

當事件源對象上發生操做時,它將會調用事件監聽器的一個方法,並在調用該方法時傳遞事件對象過去。

2.1 組成結構

Java中的事件監聽機制主要由事件源、事件對象、事件監聽器三個部分組成。

1)事件源(event source):

具體的事件源,好比說,你點擊一個button,那麼button就是event source,要想使button對某些事件進行響應,你就須要註冊特定的listener。

2)事件對象(event object):

通常繼承自java.util.EventObject類,封裝了事件源對象以及與事件相關的信息。它是在事件源和事件監聽器之間傳遞信息的。

3)事件監聽器(event listener):

實現java.util.EventListener接口,須要註冊在事件源上才能被調用。它監聽事件,並進行事件處理或者轉發。

然而,事件監聽機制與觀察者模式的關係呢?

Untitled

觀察者(Observer)至關於事件監聽者,被觀察者(Observable)或者說主題(Subject)至關於事件源和事件,執行邏輯時通知observer便可觸發oberver的update,同時可傳被觀察者和參數。

其實事件機制中的「事件對象」就至關於上例觀察者模式中的notify中的String參數對象。

2.2 舉例說明

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);
	}
}

輸出結果:

模擬門打開事件
前門打開
後門打開,警示燈亮起
模擬門關閉事件
前門關閉
後門關閉,警示燈熄滅

另外一示例:

Button 按鈕事件監聽-又說觀察者模式

參考:

一、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

五、http://ericliu1986.iteye.com/blog/629562

六、http://blog.csdn.net/xiaolang85/article/details/5316859

相關文章
相關標籤/搜索