前言
觀察者(Observer)模式也稱(chēng)發(fā)布-訂閱(Publish-Subscribe)模式,定義了對(duì)象間一種一對(duì)多的依賴(lài)關(guān)系,當(dāng)一個(gè)對(duì)象的狀態(tài)發(fā)生改變時(shí),所有依賴(lài)于它的對(duì)象都得到通知并被自動(dòng)更新。
觀察者模式的圖解如下所示:
Subject(目標(biāo)):
目標(biāo)知道它的觀察者??梢杂腥我舛鄠€(gè)觀察者觀察同一個(gè)目標(biāo)。
目標(biāo)提供了注冊(cè)和刪除觀察者對(duì)象的接口。
Observer(觀察者):
為那些在目標(biāo)發(fā)生改變時(shí)需獲得通知的對(duì)象定義一個(gè)更新接口。
ConcreteSubject(具體目標(biāo)):
將有關(guān)狀態(tài)存入各ConcreteObserver對(duì)象。
當(dāng)它的狀態(tài)發(fā)生改變時(shí),向它的各個(gè)觀察者發(fā)出通知。
ConcreteObserver(具體觀察者):
維護(hù)一個(gè)指向ConcreteSubject對(duì)象的引用。
存儲(chǔ)有關(guān)狀態(tài),這些狀態(tài)應(yīng)與目標(biāo)的狀態(tài)保存一致。
實(shí)現(xiàn)Observer的更新接口以使自身狀態(tài)與目標(biāo)的狀態(tài)保持一致。
不使用事件的示例
接下來(lái)我將根據(jù)上面的圖解寫(xiě)一個(gè)C#觀察者模式的例子,剛開(kāi)始這個(gè)例子沒(méi)有使用事件,在后面一個(gè)例子中使用事件。
首先來(lái)看看Subject(目標(biāo)):
//主題接口
public interface ISubject
{
public void Attach(Observer observer);
public void Detach(Observer observer);
public void Notify();
public string? SubjectState { get; set; }
}
這里我使用的是接口,里面有Attach、Detach和Notify方法的聲明,還有SubjectState屬性的聲明。
接下來(lái)看看ConcreteSubject(具體目標(biāo)):
public class School : ISubject
{
private IList<Observer> observers = new List<Observer>();
public string? SubjectState { get; set; }
public void Attach(Observer observer)
{
observers.Add(observer);
}
public void Detach(Observer observer)
{
observers.Remove(observer);
}
public void Notify()
{
foreach (var observer in observers)
{
observer.Update();
}
}
}
這里我以學(xué)校發(fā)通知為例,School類(lèi)實(shí)現(xiàn)了ISubject接口,有一個(gè)觀察者的列表,在Attach方法中添加一個(gè)觀察者,在Detach方法中移除一個(gè)觀察者。在Notify方法中遍歷觀察者的列表,讓每一個(gè)觀察者都執(zhí)行Update方法。
現(xiàn)在來(lái)看看Observer(觀察者):
public abstract class Observer
{
protected string? name;
protected ISubject? subject;
public Observer(string name,ISubject subject)
{
this.name = name;
this.subject = subject;
}
public abstract void Update();
}
這是一個(gè)抽象類(lèi),包含有一個(gè)抽象的Update方法。
再看看ConcreteObserver(具體觀察者):
public class Student : Observer
{
public Student(string name,ISubject subject) : base(name,subject)
{
}
public override void Update()
{
Console.WriteLine($"{name}接收到來(lái)自學(xué)校的通知:{subject?.SubjectState},
時(shí)間:{DateTime.Now}\r\n");
}
}
這里我以學(xué)生接收來(lái)自學(xué)校的消息為例,Student類(lèi)繼承自O(shè)bserver抽象類(lèi),并重寫(xiě)了Update方法。
最后來(lái)看看怎么使用觀察者模式:
static void Main(string[] args)
{
School school = new School();
Student student1 = new Student("小王", school);
Student student2 = new Student("小明", school);
Student student3 = new Student("小紅", school);
school.Attach(student1);
school.Attach(student2);
school.Attach(student3);
school.SubjectState = "學(xué)校放假了";
school.Notify();
school.Detach(student3);
Task.Delay(3000).Wait();
school.SubjectState = "學(xué)校開(kāi)學(xué)了";
school.Notify();
Console.ReadLine();
}
創(chuàng)建一個(gè)School對(duì)象,三個(gè)Student對(duì)象。
school.Attach(student1);
school.Attach(student2);
school.Attach(student3);
表示將student1、student2、student3添加到school中的觀察者列表中。
school.SubjectState = "學(xué)校放假了";
school.Notify();
設(shè)置school中SubjectState屬性的值,然后調(diào)用school的Notify方法。
school.Detach(student3);
將student3從school的觀察者列表中移除。
Task.Delay(3000).Wait();
等待3秒。
school.SubjectState = "學(xué)校開(kāi)學(xué)了";
school.Notify();
Console.ReadLine();
重新設(shè)置school中SubjectState屬性的值,然后再調(diào)用school的Notify方法。
運(yùn)行結(jié)果如下所示:
學(xué)校放假了,小王、小明和小紅都接收到了學(xué)校的通知。由于后面小紅被移出了觀察者列表,因此學(xué)校開(kāi)學(xué)了的消息小紅沒(méi)有接收到。
使用事件的示例
C#中可以通過(guò)事件來(lái)使用觀察者模式,接下來(lái)我將以一個(gè)示例加以說(shuō)明。
自定義事件數(shù)據(jù)類(lèi):
public class SendMessageArgs : EventArgs
{
public string? Message { get; set; }
public DateTime DateTime { get; set; }
public SendMessageArgs(string? message)
{
Message = message;
DateTime = DateTime.Now;
}
}
Person類(lèi):
public class Person
{
public string? Name { get; set; }
public event EventHandler<SendMessageArgs>? SendMessageEvent;
public Person(string? name)
{
Name = name;
}
public void SendMessage(string message)
{
SendMessageArgs sendMessageArgs = new SendMessageArgs(message);
SendMessageEvent?.Invoke(this, sendMessageArgs);
}
public void ShowMessage(object? sender,SendMessageArgs e)
{
Person? person = (Person?)sender;
Console.WriteLine($"{this.Name}:收到來(lái)自{person?.Name}的消息:{e.Message},時(shí)間:{e.DateTime}\r\n");
}
public void Subscribe(Person person)
{
person.SendMessageEvent += ShowMessage;
}
public void UnSubscribe(Person person)
{
person.SendMessageEvent -= ShowMessage;
}
}
Person類(lèi)中的SendMessage方法會(huì)觸發(fā)事件,ShowMessage方法是事件處理程序,Subscribe方法可以訂閱事件,UnSubscribe方法可以取消訂閱事件。
現(xiàn)在來(lái)看看是怎么使用:
static void Main(string[] args)
{
Person Trump = new Person("川普");
Person Biden = new Person("拜登");
Person person1 = new Person("person1");
Person person2 = new Person("person2");
Person person3 = new Person("person3");
person1.Subscribe(Trump);
person2.Subscribe(Trump);
person3.Subscribe(Trump);
person1.Subscribe(Biden);
Trump.SendMessage("Nobody knows ... better than me!!!");
Task.Delay(2000).Wait();
Biden.SendMessage("I don't believe it!!!");
person3.UnSubscribe(Trump);
Task.Delay(2000).Wait();
Trump.SendMessage("Make ... Great Again!!!");
Console.ReadLine();
}
創(chuàng)建了5個(gè)Person對(duì)象,分別為T(mén)rump、Biden、person1、person2、person3。
person1、person2、person3訂閱了Trump,person1訂閱了Biden。
Trump發(fā)了一條消息,然后過(guò)了2秒,Biden也發(fā)了一條消息。
person3不再訂閱Trump,過(guò)了2秒,Trump又發(fā)消息了。
運(yùn)行結(jié)果如下所示:
由于person1、person2、person3訂閱了Trump,所以可以收到來(lái)自Trump的消息。
由于person1訂閱了Biden,所以可以收到來(lái)自Biden的消息。
后面person3退訂了Trump,所以只有person1、person2能收到來(lái)自Trump的消息。
總結(jié)
以上使用C#分別創(chuàng)建了不通過(guò)事件與通過(guò)事件的示例,介紹了在C#中如何使用觀察者模式,希望對(duì)你有所幫助。
參考
1、《Head First 設(shè)計(jì)模式(中文版)》
2、《大話(huà)設(shè)計(jì)模式》
3、《設(shè)計(jì)模式:可復(fù)用面向?qū)ο筌浖幕A(chǔ)》文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-777000.html
4、YouTube [Design Patterns: C# Pub-Sub Simple Twitter example]文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-777000.html
到了這里,關(guān)于C#設(shè)計(jì)模式之觀察者模式的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!