- 概述
定義對(duì)象之間的一種一對(duì)多依賴關(guān)系削咆,使得每當(dāng)一個(gè)對(duì)象狀態(tài)發(fā)生改變時(shí)号坡,其相關(guān)依賴對(duì)象皆得到通知并被自動(dòng)更新欧聘。觀察者模式的別名包括發(fā)布-訂閱(Publish/Subscribe)模式忍捡、模型-視圖(Model/View)模式形葬、源-監(jiān)聽(tīng)器(Source/Listener)模式或從屬者(Dependents)模式擦秽。觀察者模式是一種對(duì)象行為型模式码荔。
2. 模式中的角色
2.1 Subject(抽象目標(biāo)):目標(biāo)又稱為主題,它是指被觀察的對(duì)象感挥。在目標(biāo)中定義了一個(gè)觀察者集合缩搅,一個(gè)觀察目標(biāo)可以接受任意數(shù)量的觀察者來(lái)觀察,它提供一系列方法來(lái)增加和刪除觀察者對(duì)象触幼,同時(shí)它定義了通知方法notify()硼瓣。目標(biāo)類可以是接口,也可以是抽象類或具體類置谦。
2.2 ConcreteSubject(具體目標(biāo)):具體目標(biāo)是目標(biāo)類的子類堂鲤,通常它包含有經(jīng)常發(fā)生改變的數(shù)據(jù),當(dāng)它的狀態(tài)發(fā)生改變時(shí)媒峡,向它的各個(gè)觀察者發(fā)出通知瘟栖;同時(shí)它還實(shí)現(xiàn)了在目標(biāo)類中定義的抽象業(yè)務(wù)邏輯方法(如果有的話)。如果無(wú)須擴(kuò)展目標(biāo)類谅阿,則具體目標(biāo)類可以省略半哟。
2.3 Observer(抽象觀察者):觀察者將對(duì)觀察目標(biāo)的改變做出反應(yīng),觀察者一般定義為接口签餐,該接口聲明了更新數(shù)據(jù)的方法update()寓涨,因此又稱為抽象觀察者。
2.4 ConcreteObserver(具體觀察者):在具體觀察者中維護(hù)一個(gè)指向具體目標(biāo)對(duì)象的引用贱田,它存儲(chǔ)具體觀察者的有關(guān)狀態(tài)缅茉,這些狀態(tài)需要和具體目標(biāo)的狀態(tài)保持一致;它實(shí)現(xiàn)了在抽象觀察者Observer中定義的update()方法男摧。通常在實(shí)現(xiàn)時(shí)蔬墩,可以調(diào)用具體目標(biāo)類的attach()方法將自己添加到目標(biāo)類的集合中或通過(guò)detach()方法將自己從目標(biāo)類的集合中刪除。
3. 模式解讀
3.1 模式的類圖
[圖片上傳失敗...(image-4ed9c7-1649316690458)]
3.2 代碼實(shí)現(xiàn)
using System;
using System.Collections.Generic;
namespace ConsoleApp2
{
class Class16
{
public static void Main(string[] args)
{
//定義觀察目標(biāo)對(duì)象
Team acc = new ConcreteTeam("金庸群俠");
//定義四個(gè)觀察者對(duì)象
IObserver player1, player2, player3, player4;
player1 = new Player("楊過(guò)");
acc.Register(player1);
player2 = new Player("令狐沖");
acc.Register(player2);
player3 = new Player("張無(wú)忌");
acc.Register(player3);
player4 = new Player("段譽(yù)");
acc.Register(player4);
//某成員遭受攻擊
player1.BeAttacked(acc);
Console.ReadLine();
}
}
// 抽象觀察類
public interface IObserver
{
string GetName();
void Help(); //聲明支援盟友方法
void BeAttacked(Team acc); //聲明遭受攻擊方法
}
// 戰(zhàn)隊(duì)成員類:具體觀察者類
public class Player : IObserver
{
private string name;
public Player(string name)
{
this.name = name;
}
public string GetName()
{
return name;
}
// 支援盟友方法的實(shí)現(xiàn)
public void Help()
{
Console.WriteLine("堅(jiān)持住耗拓," + name + "馬上來(lái)救你拇颅!");
}
// 遭受攻擊方法的實(shí)現(xiàn),當(dāng)遭受攻擊時(shí)將調(diào)用戰(zhàn)隊(duì)類的通知方法NotifyObserver()來(lái)通知盟友
public void BeAttacked(Team acc)
{
Console.WriteLine(name + "被攻擊乔询!");
acc.NotifyObserver(name);
}
}
// 戰(zhàn)隊(duì):抽象目標(biāo)類
public abstract class Team
{
protected string teamName; //戰(zhàn)隊(duì)名稱
protected List<IObserver> players = new List<IObserver>(); //定義一個(gè)集合用于存儲(chǔ)戰(zhàn)隊(duì)成員
public void SetTeamName(string teamName)
{
this.teamName = teamName;
}
public string GetTeamName()
{
return teamName;
}
// 注冊(cè)方法
public void Register(IObserver obs)
{
Console.WriteLine(obs.GetName() + "加入" + teamName + "戰(zhàn)隊(duì)樟插!");
players.Add(obs);
}
// 注銷方法
public void Remove(IObserver obs)
{
Console.WriteLine(obs.GetName() + "退出" + teamName + "戰(zhàn)隊(duì)!");
players.Remove(obs);
}
// 聲明抽象通知方法
public abstract void NotifyObserver(string name);
}
// 具體戰(zhàn)隊(duì)類:具體目標(biāo)類
public class ConcreteTeam : Team
{
public ConcreteTeam(string teamName)
{
Console.WriteLine(teamName + "戰(zhàn)隊(duì)組建成功!");
Console.WriteLine("----------------------------");
this.teamName = teamName;
}
// 實(shí)現(xiàn)通知方法
public override void NotifyObserver(string name)
{
Console.WriteLine(teamName + "戰(zhàn)隊(duì)緊急通知黄锤,盟友" + name + "遭受敵人攻擊搪缨!");
// 遍歷觀察者集合,調(diào)用每一個(gè)盟友(自己除外)的支援方法
foreach (var obs in players)
{
if (!(obs.GetName().Equals(name, StringComparison.CurrentCultureIgnoreCase)))
{
obs.Help();
}
}
}
}
}
4鸵熟、模式的優(yōu)缺點(diǎn)
4.1 優(yōu)點(diǎn):
- 觀察者模式在觀察目標(biāo)和觀察者之間建立一個(gè)抽象的耦合副编。觀察目標(biāo)只需要維持一個(gè)抽象觀察者的集合,無(wú)須了解其具體觀察者流强。由于觀察目標(biāo)和觀察者沒(méi)有緊密地耦合在一起痹届,因此它們可以屬于不同的抽象化層次。打月。
- 觀察者模式支持“廣播通信”队腐。主題會(huì)向所有的觀察者發(fā)出通知。
- 觀察者模式符合“開閉原則”的要求奏篙,增加新的具體觀察者無(wú)須修改原有系統(tǒng)代碼秘通,在具體觀察者與觀察目標(biāo)之間不存在關(guān)聯(lián)關(guān)系的情況下荸型,增加新的觀察目標(biāo)也很方便稿静。
4.2 缺點(diǎn):
- 如果一個(gè)被觀察者對(duì)象有很多的直接和間接的觀察者的話辕狰,將所有的觀察者都通知到會(huì)花費(fèi)很多時(shí)間改备。
- 如果在觀察者和觀察目標(biāo)之間有循環(huán)依賴的話蔓倍,觀察目標(biāo)會(huì)觸發(fā)它們之間進(jìn)行循環(huán)調(diào)用悬钳,可能導(dǎo)致系統(tǒng)崩潰。
- 觀察者模式?jīng)]有相應(yīng)的機(jī)制讓觀察者知道所觀察的目標(biāo)對(duì)象是怎么發(fā)生變化的,而僅僅只是知道觀察目標(biāo)發(fā)生了變化。
5习霹、模式適用場(chǎng)景
- 一個(gè)抽象模型有兩個(gè)方面淋叶,其中一個(gè)方面依賴于另一個(gè)方面望门。將這些方面封裝在獨(dú)立的對(duì)象中使它們可以各自獨(dú)立地改變和復(fù)用筹误。
- 一個(gè)對(duì)象的改變將導(dǎo)致其他一個(gè)或多個(gè)對(duì)象也發(fā)生改變,而不知道具體有多少對(duì)象將發(fā)生改變,可以降低對(duì)象之間的耦合度直晨。
- 一個(gè)對(duì)象必須通知其他對(duì)象门烂,而并不知道這些對(duì)象是誰(shuí)。需要在系統(tǒng)中創(chuàng)建一個(gè)觸發(fā)鏈兄淫,A對(duì)象的行為將影響B(tài)對(duì)象氓润,B對(duì)象的行為將影響C對(duì)象……薯鳍,可以使用觀察者模式創(chuàng)建一種鏈?zhǔn)接|發(fā)機(jī)制。