设计模式-观察者模式(Observer)
讲故事(user story)
假设我们是一个优惠券提供平台,故事就发生在顾客在我们平台采购完成支付成功后。
支付完成后平台要进行的一些操作:
-
短信通知客户已经生成订单
-
增加顾客的积分
-
开始按订单需求制券
。。。(可能会有许多操作)
接下来就是将故事以代码的形式展现出来。。。
需求分析
我们将上述故事转化为代码中的对象分别是: 支付成功 PaySuccessSubject、短信通知 MessageObserver、积分增加 BonusObserver、制券 CouponObserver。
当支付成功PaySuccessSubject后,要通知到MessageObserver,BonusObserver,CouponObserver这三个对象,为了实现上面这个需求,将采用观察者模式(发布-订阅)
敲黑板.划重点
观察者模式又叫发布-订阅(Publish/Subscribe)模式,定义了一种一对多的依赖关系,让多个观察者对象同时 监听某一个主题对象。当主题对象在状态发生变化时,通知所有观察者对象,使他们能够自己更新自己。
Show Code
Subject类,把所有观察者对象的引用保存在一个集合了,每个通知者都可以有任何数量的观察者。抽象通知者提供 可以增加和删除观察者对象的接口。
///
/// 抽象通知者 /// public abstract class Subject { ////// 观察者集合 /// protected Listobservers = new List (); public string State { get; set; } /// /// 添加观察者 /// /// 观察者 public void Attach(IObserver observer) { observers.Add(observer); } ////// 删除观察者 /// /// 观察者 public void Detach(IObserver observer) { observers.Remove(observer); } ////// 通知 /// ///public void Notify() { foreach (var observer in observers) { observer.Update(); } } }
PaySuccessSubject类,具体的通知者,给所有登记过的观察者发出通知。
///
/// 支持成功通知者 /// public class PaySuccessSubject : Subject { }
Observer类,抽象观察者,为所有的具体的观察者定义一个接口,一般用抽象类或接口实现。通常包含一个Update()更新方法。
///
/// 抽象观察 /// public abstract class Observer { public abstract void Update(); }
MessageObserver、BonusObserver、CouponObserver具体的观察者类,实现更新接口,以便本身的状态与主题的状态相协调。
///
/// 短信观察者 /// public class MessageObserver : Observer { public Subject Subject { get; set; } public MessageObserver(Subject subject) { Subject = subject; } public override void Update() { Console.WriteLine($"{Subject.State}:短信通知了..."); } } ////// 积分观察者 /// public class BonusObserver : Observer { public Subject Subject { get; set; } public BonusObserver(Subject subject) { Subject = subject; } public override void Update() { Console.WriteLine($"{Subject.State}:积分增加了..."); } } ////// 券观察者 /// 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(" Happy Ending~"); Console.ReadLine(); }
结果显示
Code Upgrade
code review后发现,在通知给观察者时,是顺序执行,如果其中一个观察者卡顿或者错误,会导致其他观察者卡克,所以我们应该采用异步方式。
下面我们将模拟 制券过程耗时增加,但不影响通知其他观察者。直接上代码:
///
/// 抽象观察
///
public abstract class Observer
{
public abstract Task UpdateAsync();
}
///
/// 短信观察者
///
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;
}
}
///
/// 积分观察者
///
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;
}
}
///
/// 券观察者
///
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}:制券完成...");
}
}
///
/// 抽象通知者
///
public abstract class Subject
{
///
/// 观察者集合
///
protected List observers = new List();
public string State { get; set; }
///
/// 添加观察者
///
/// 观察者
public void Attach(Observer observer)
{
observers.Add(observer);
}
///
/// 删除观察者
///
/// 观察者
public void Detach(Observer observer)
{
observers.Remove(observer);
}
///
/// 通知
///
///
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("
Happy Ending~");
Console.ReadLine();
}
结果显示:
委托加持观察者模式
现实开发中,很多观察者对象共同继承或者实现同一个抽象观察者,不合适;并且所有观察者对象的操作方法统一叫一个 Update(),达不到望文生义的效果,所以我们对观察者模式再次进行升级,使用委托来替换掉抽象观察者,
直接上代码:
///
/// 短信观察者
///
public class MessageObserver
{
public ISubject Subject { get; set; }
public MessageObserver(ISubject subject)
{
Subject = subject;
}
///
/// 发送短信
///
///
public Task SendMessageAsync()
{
Console.WriteLine($"{Subject.State}:短信通知了...");
return Task.CompletedTask;
}
}
///
/// 积分观察者
///
public class BonusObserver
{
public ISubject Subject { get; set; }
public BonusObserver(ISubject subject)
{
Subject = subject;
}
///
/// 添加积分
///
///
public Task AddBonusAsync()
{
Console.WriteLine($"{Subject.State}:积分增加了...");
return Task.CompletedTask;
}
}
///
/// 券观察者
///
public class CouponObserver
{
public ISubject Subject { get; set; }
public CouponObserver(ISubject subject)
{
Subject = subject;
}
///
/// 制券
///
///
public async Task MakeCouponAsync()
{
Console.WriteLine($"{Subject.State}:开始制券...");
//模拟制券耗时
await Task.Delay(3000);
Console.WriteLine($"{Subject.State}:制券完成...");
}
}
///
/// 抽象通知者
///
public interface ISubject
{
///
/// 通知
///
///
public Task Notify();
public string State { get; set; }
}
///
/// 支持成功通知者
///
public class PaySuccessSubject : ISubject
{
public Func 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("
Happy Ending~");
Console.ReadLine();
}
}
}
展示结果和上面一样。
源码地址:https://gitee.com/sayook/DesignMode
免责声明:
① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。
② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341