设计模式观察者模式(Observer)

database

讲故事(user story)

假设我们是一个优惠券提供平台,故事就发生在顾客在我们平台采购完成支付成功后。

支付完成后平台要进行的一些操作:

  1. 短信通知客户已经生成订单

  2. 增加顾客的积分

  3. 开始按订单需求制券

​ 。。。(可能会有许多操作)

接下来就是将故事以代码的形式展现出来。。。

需求分析

我们将上述故事转化为代码中的对象分别是: 支付成功 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("

Happy 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("

Happy 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("

Happy Ending~");

Console.ReadLine();

}

}

}

展示结果和上面一样。

源码地址:https://gitee.com/sayook/DesignMode

以上是 设计模式观察者模式(Observer) 的全部内容, 来源链接: utcz.com/z/533264.html

回到顶部