1. 觀察者模式
1.1 簡(jiǎn)介
??觀察者(Observer)模式的定義:指多個(gè)對(duì)象間存在一對(duì)多的依賴關(guān)系,當(dāng)一個(gè)對(duì)象的狀態(tài)發(fā)生改變時(shí)枉氮,所有依賴于它的對(duì)象都得到通知并被自動(dòng)更新暖庄。這種模式有時(shí)又稱作發(fā)布-訂閱模式、模型-視圖模式惹悄,它是對(duì)象行為型模式肩钠。
??Observer并非主動(dòng)觀察暂殖,而是被動(dòng)觀察当纱。觀察者模式可以理解為發(fā)布-訂閱模式,即多個(gè)訂閱者(觀察者)向發(fā)布者(被觀察者)訂閱狀態(tài)信息莉给,當(dāng)發(fā)布者更新?tīng)顟B(tài)時(shí)會(huì)將狀態(tài)信息向它的訂閱者發(fā)布信息廉沮。發(fā)布者需要自己維護(hù)訂閱者列表,可以注冊(cè)或者注銷(xiāo)對(duì)狀態(tài)信息感興趣或不感興趣的訂閱者叁幢。
1.2 Observer模式結(jié)構(gòu)
模式結(jié)構(gòu):
模式角色:
- 抽象主題(Subject)角色:也叫抽象目標(biāo)類坪稽,它提供了一個(gè)用于保存觀察者對(duì)象的聚集類和增加、刪除觀察者對(duì)象的方法黍判,以及通知所有觀察者的抽象方法篙梢。
- 具體主題(Concrete Subject)角色:也叫具體目標(biāo)類,它實(shí)現(xiàn)抽象目標(biāo)中的通知方法渤滞,當(dāng)具體主題的內(nèi)部狀態(tài)發(fā)生改變時(shí),通知所有注冊(cè)過(guò)的觀察者對(duì)象陶舞。
- 抽象觀察者(Observer)角色:它是一個(gè)抽象類或接口绪励,它包含了一個(gè)更新自己的抽象方法,當(dāng)接到具體主題的更改通知時(shí)被調(diào)用停做。
- 具體觀察者(Concrete Observer)角色:實(shí)現(xiàn)抽象觀察者中定義的抽象方法蠢护,以便在得到目標(biāo)的更改通知時(shí)更新自身的狀態(tài)养涮。
2. Observer模式示例
??我們模擬一個(gè)學(xué)校上下課鈴聲的事件處理過(guò)程眉抬,老師和學(xué)生聽(tīng)到上課鈴聲準(zhǔn)備上課懈凹,聽(tīng)到下課鈴聲準(zhǔn)備下課。學(xué)校的“鈴”是事件源和目標(biāo)库北,“老師”和“學(xué)生”是事件監(jiān)聽(tīng)器和具體觀察者,“鈴聲”是事件類寒瓦。
鈴聲事件處理程序uml:
鈴聲事件類:
public class RingEvent
{
private boolean sound; //true表示上課鈴聲,false表示下課鈴聲
public RingEvent(boolean sound)
{
this.sound=sound;
}
public void setSound(boolean sound)
{
this.sound=sound;
}
public boolean getSound()
{
return this.sound;
}
}
鈴聲目標(biāo)類:
public class BellEventSource
{
private List<BellEventListener> listener;
public BellEventSource()
{
listener=new ArrayList<BellEventListener>();
}
//給事件源綁定監(jiān)聽(tīng)器
public void addPersonListener(BellEventListener ren)
{
listener.add(ren);
}
//事件觸發(fā)器:敲鐘杂腰,當(dāng)鈴聲sound的值發(fā)生變化時(shí)椅文,觸發(fā)事件。
public void ring(boolean sound)
{
String type=sound?"上課鈴":"下課鈴";
System.out.println(type+"響少辣!");
RingEvent event=new RingEvent(sound);
notifies(event); //通知注冊(cè)在該事件源上的所有監(jiān)聽(tīng)器
}
//當(dāng)事件發(fā)生時(shí),通知綁定在該事件源上的所有監(jiān)聽(tīng)器做出反應(yīng)(調(diào)用事件處理方法)
protected void notifies(RingEvent e)
{
BellEventListener ren=null;
Iterator<BellEventListener> iterator=listener.iterator();
while(iterator.hasNext())
{
ren=iterator.next();
ren.heardBell(e);
}
}
}
觀察者接口:
interface BellEventListener extends EventListener
{
//事件處理方法羡蛾,聽(tīng)到鈴聲
public void heardBell(RingEvent e);
}
監(jiān)聽(tīng)者老師:
public class TeachEventListener implements BellEventListener
{
public void heardBell(RingEvent e)
{
if(e.getSound())
{
System.out.println("老師開(kāi)始上課...");
}
else
{
System.out.println("老師下課...");
}
}
}
監(jiān)聽(tīng)者學(xué)生:
public class StuEventListener implements BellEventListener
{
public void heardBell(RingEvent e)
{
if(e.getSound())
{
System.out.println("同學(xué)們,上課了...");
}
else
{
System.out.println("同學(xué)們林说,下課了...");
}
}
}
客戶端調(diào)用:
public static void main(String[] args)
{
BellEventSource bell=new BellEventSource();
bell.addPersonListener(new TeachEventListener()); //注冊(cè)監(jiān)聽(tīng)器(老師)
bell.addPersonListener(new StuEventListener()); //注冊(cè)監(jiān)聽(tīng)器(學(xué)生)
bell.ring(true);
bell.ring(false); //打下課鈴聲
}
結(jié)果:
上課鈴響!
老師開(kāi)始上課...
同學(xué)們豪直,上課了...
下課鈴響珠移!
老師下課...
同學(xué)們,下課了...
3. Observer模式總結(jié)
Observer模式優(yōu)點(diǎn):
- 降低了目標(biāo)與觀察者之間的耦合關(guān)系钧惧,兩者之間是抽象耦合關(guān)系。
- 目標(biāo)與觀察者之間建立了一套觸發(fā)機(jī)制懈玻。
Observer模式缺點(diǎn):
- 目標(biāo)與觀察者之間的依賴關(guān)系并沒(méi)有完全解除乾颁,而且有可能出現(xiàn)循環(huán)引用艺栈。
- 當(dāng)觀察者對(duì)象很多時(shí)湾盒,通知的發(fā)布會(huì)花費(fèi)很多時(shí)間,影響程序的效率毅人。
Observer模式應(yīng)用:
-
MVC模式,Model丈莺、View送丰、Controller,并且Model里面的操作不依賴于具體形式的內(nèi)部模型蚪战,通常情況下:
一個(gè)Model對(duì)應(yīng)多個(gè)View,這里也是使用Observer設(shè)計(jì)模式最多的地方
Observer模式與Mediator模式:
- 在Mediator模式中瞎疼,有時(shí)會(huì)使用Observer 模式來(lái)實(shí)現(xiàn)Mediator角色與Colleague角色之間的通信壁畸。
- 就“發(fā)送狀態(tài)變化通知”這一點(diǎn)而言贼急,Mediator模式與Observer模式是類似的太抓。不過(guò),兩種模式中走敌,通知的目的和視角不同逗噩。
- 在Mediator模式中,雖然也會(huì)發(fā)送通知异雁,不過(guò)那不過(guò)是為了對(duì)Colleague角色進(jìn)行仲裁而已。
而在Observer模式中项炼,將Subject角色的狀態(tài)變化通知給Observer角色的目的則主要是為了使Subject角色和Observer角色同步。