设计模式-观察者模式(Observer)
讲故事(user story)
假设我们是一个优惠券提供平台,故事就发生在顾客在我们平台采购完成支付成功后。
支付完成后平台要进行的一些操作:
-
短信通知客户已经生成订单
-
增加顾客的积分
-
开始按订单需求制券
。。。(可能会有许多操作)
接下来就是将故事以代码的形式展现出来。。。
需求分析
我们将上述故事转化为代码中的对象分别是: 支付成功 paysuccesssubject、短信通知 messageobserver、积分增加 bonusobserver、制券 couponobserver。
当支付成功paysuccesssubject后,要通知到messageobserver,bonusobserver,couponobserver这三个对象,为了实现上面这个需求,将采用观察者模式(发布-订阅)
敲黑板.划重点
观察者模式又叫发布-订阅(publish/subscribe)模式,定义了一种一对多的依赖关系,让多个观察者对象同时 监听某一个主题对象。当主题对象在状态发生变化时,通知所有观察者对象,使他们能够自己更新自己。
show code
subject类,把所有观察者对象的引用保存在一个集合了,每个通知者都可以有任何数量的观察者。抽象通知者提供 可以增加和删除观察者对象的接口。
/// <summary> /// 抽象通知者 /// </summary> public abstract class subject { /// <summary> /// 观察者集合 /// </summary> protected list<iobserver> observers = new list<iobserver>(); public string state { get; set; } /// <summary> /// 添加观察者 /// </summary> /// <param name="observer">观察者</param> public void attach(iobserver observer) { observers.add(observer); } /// <summary> /// 删除观察者 /// </summary> /// <param name="observer">观察者</param> public void detach(iobserver observer) { observers.remove(observer); } /// <summary> /// 通知 /// </summary> /// <returns></returns> public void notify() { foreach (var observer in observers) { observer.update(); } } }
paysuccesssubject类,具体的通知者,给所有登记过的观察者发出通知。
/// <summary> /// 支持成功通知者 /// </summary> public class paysuccesssubject : subject { }
observer类,抽象观察者,为所有的具体的观察者定义一个接口,一般用抽象类或接口实现。通常包含一个update()更新方法。
/// <summary> /// 抽象观察 /// </summary> public abstract class observer { public abstract void update(); }
messageobserver、bonusobserver、couponobserver具体的观察者类,实现更新接口,以便本身的状态与主题的状态相协调。
/// <summary> /// 短信观察者 /// </summary> public class messageobserver : observer { public subject subject { get; set; } public messageobserver(subject subject) { subject = subject; } public override void update() { console.writeline($"{subject.state}:短信通知了..."); } } /// <summary> /// 积分观察者 /// </summary> public class bonusobserver : observer { public subject subject { get; set; } public bonusobserver(subject subject) { subject = subject; } public override void update() { console.writeline($"{subject.state}:积分增加了..."); } } /// <summary> /// 券观察者 /// </summary> public class couponobserver : observer { public subject subject { get; set; } public couponobserver(subject subject) { subject = subject; } public override void update() { console.writeline($"{subject.state}:开始制券了..."); } }
客户端代码
private static void main(string[] args) { var subject = new paysuccesssubject(); var observer1 = new couponobserver(subject); var observer2 = new messageobserver(subject); var observer3 = new bonusobserver(subject); //添加订阅 subject.attach(observer1); subject.attach(observer2); subject.attach(observer3); //发布通知 subject.state = "星巴克10十元券采购成功"; subject.notify(); console.writeline("\n\nhappy ending~"); console.readline(); }
结果显示
code upgrade
code review后发现,在通知给观察者时,是顺序执行,如果其中一个观察者卡顿或者错误,会导致其他观察者卡克,所以我们应该采用异步方式。
下面我们将模拟 制券过程耗时增加,但不影响通知其他观察者。直接上代码:
/// <summary> /// 抽象观察 /// </summary> public abstract class observer { public abstract task updateasync(); } /// <summary> /// 短信观察者 /// </summary> public class messageobserver : observer { public subject subject { get; set; } public messageobserver(subject subject) { subject = subject; } public override task updateasync() { console.writeline($"{subject.state}:短信通知了..."); return task.completedtask; } } /// <summary> /// 积分观察者 /// </summary> public class bonusobserver : observer { public subject subject { get; set; } public bonusobserver(subject subject) { subject = subject; } public override task updateasync() { console.writeline($"{subject.state}:积分增加了..."); return task.completedtask; } } /// <summary> /// 券观察者 /// </summary> public class couponobserver : observer { public subject subject { get; set; } public couponobserver(subject subject) { subject = subject; } public override async task updateasync() { console.writeline($"{subject.state}:开始制券..."); //模拟制券耗时 await task.delay(3000); console.writeline($"{subject.state}:制券完成..."); } } /// <summary> /// 抽象通知者 /// </summary> public abstract class subject { /// <summary> /// 观察者集合 /// </summary> protected list<observer> observers = new list<observer>(); public string state { get; set; } /// <summary> /// 添加观察者 /// </summary> /// <param name="observer">观察者</param> public void attach(observer observer) { observers.add(observer); } /// <summary> /// 删除观察者 /// </summary> /// <param name="observer">观察者</param> public void detach(observer observer) { observers.remove(observer); } /// <summary> /// 通知 /// </summary> /// <returns></returns> public task notify() { foreach (var observer in observers) { observer.updateasync(); } return task.completedtask; } }
客户端端代码:
private static async task main(string[] args) { var subject = new paysuccesssubject(); var observer1 = new couponobserver(subject); var observer2 = new messageobserver(subject); var observer3 = new bonusobserver(subject); //添加订阅 subject.attach(observer1); subject.attach(observer2); subject.attach(observer3); //发布通知 subject.state = "星巴克10十元券采购成功"; await subject.notify(); console.writeline("\n\nhappy ending~"); console.readline(); }
结果显示:
委托加持观察者模式
现实开发中,很多观察者对象共同继承或者实现同一个抽象观察者,不合适;并且所有观察者对象的操作方法统一叫一个 update(),达不到望文生义的效果,所以我们对观察者模式再次进行升级,使用委托来替换掉抽象观察者,
直接上代码:
/// <summary> /// 短信观察者 /// </summary> public class messageobserver { public isubject subject { get; set; } public messageobserver(isubject subject) { subject = subject; } /// <summary> /// 发送短信 /// </summary> /// <returns></returns> public task sendmessageasync() { console.writeline($"{subject.state}:短信通知了..."); return task.completedtask; } } /// <summary> /// 积分观察者 /// </summary> public class bonusobserver { public isubject subject { get; set; } public bonusobserver(isubject subject) { subject = subject; } /// <summary> /// 添加积分 /// </summary> /// <returns></returns> public task addbonusasync() { console.writeline($"{subject.state}:积分增加了..."); return task.completedtask; } } /// <summary> /// 券观察者 /// </summary> public class couponobserver { public isubject subject { get; set; } public couponobserver(isubject subject) { subject = subject; } /// <summary> /// 制券 /// </summary> /// <returns></returns> public async task makecouponasync() { console.writeline($"{subject.state}:开始制券..."); //模拟制券耗时 await task.delay(3000); console.writeline($"{subject.state}:制券完成..."); } } /// <summary> /// 抽象通知者 /// </summary> public interface isubject { /// <summary> /// 通知 /// </summary> /// <returns></returns> public task notify(); public string state { get; set; } } /// <summary> /// 支持成功通知者 /// </summary> public class paysuccesssubject : isubject { public func<task> update; public string state { get; set; } public task notify() { update(); return task.completedtask; } } }
客户端调用:
internal class program { private static async task main(string[] args) { var subject = new observerdelegate.paysuccesssubject(); var observer1 = new observerdelegate.couponobserver(subject); var observer2 = new observerdelegate.messageobserver(subject); var observer3 = new observerdelegate.bonusobserver(subject); //添加订阅 subject.update += observer1.makecouponasync; subject.update += observer2.sendmessageasync; subject.update += observer3.addbonusasync; //发布通知 subject.state = "星巴克10十元券采购成功"; await subject.notify(); console.writeline("\n\nhappy ending~"); console.readline(); } } }
展示结果和上面一样。