Storytelling
Assuming that we are a coupon offering platform, the story happens after the customer has successfully paid for the purchase of our platform.
Some actions to be done by the platform after the payment is completed:
-
SMS informs customer that an order has been generated
-
Increase customer credit
-
Start vouching on order
....(There may be many operations)
The next step is to show the story in code.
requirement analysis
We translated the above stories into code for successful PaySuccessSubject, SMS Notification MessageObserver, BonusObserver for credit increase, CouponObserver for vouchers.
To notify MessageObserver, BonusObserver, CouponObserver when PaySuccessSubject has been successfully paid, the Observer mode (publish-subscribe) will be used to fulfill this requirement.
Knock on the blackboard. Draw the focus
The observer mode, also known as the Publish/Subscribe mode, defines a one-to-many dependency in which multiple observer objects listen to a subject object at the same time.Notify all observer objects when their state changes so that they can update themselves.
Show Code
Subjectclass,Save references to all observer objects in one collection,Each notifier can have any number of observers.Abstract notifiers provide interfaces that can add and delete observer objects.
/// <summary> ///Abstract Notifier /// </summary> public abstract class Subject { /// <summary> ///Observer Set /// </summary> protected List<IObserver> observers = new List<IObserver>(); public string State { get; set; } /// <summary> ///Add an observer /// </summary> /// <param name="observer">observer</param> public void Attach(IObserver observer) { observers.Add(observer); } /// <summary> ///Delete observer /// </summary> /// <param name="observer">observer</param> public void Detach(IObserver observer) { observers.Remove(observer); } /// <summary> ///Notification /// </summary> /// <returns></returns> public void Notify() { foreach (var observer in observers) { observer.Update(); } } }
PaySuccessSubjectClass, specific notifier, notifies all registered observers.
/// <summary> ///Supports Success Notifiers /// </summary> public class PaySuccessSubject : Subject { }
ObserverClass, Abstract observer, defines an interface for all specific observers, generally implemented with an abstract class or interface.Usually contains one Update()Update method.
/// <summary> ///abstract observation /// </summary> public abstract class Observer { public abstract void Update(); }
MessageObserver,BonusObserver,CouponObserverSpecific observer classes that implement update interfaces to coordinate their own state with that of the subject.
/// <summary> ///SMS watcher /// </summary> public class MessageObserver : Observer { public Subject Subject { get; set; } public MessageObserver(Subject subject) { Subject = subject; } public override void Update() { Console.WriteLine($"{Subject.State}:SMS Notification..."); } } /// <summary> ///Integral Observer /// </summary> public class BonusObserver : Observer { public Subject Subject { get; set; } public BonusObserver(Subject subject) { Subject = subject; } public override void Update() { Console.WriteLine($"{Subject.State}:Integral increased..."); } } /// <summary> ///Voucher watcher /// </summary> public class CouponObserver : Observer { public Subject Subject { get; set; } public CouponObserver(Subject subject) { Subject = subject; } public override void Update() { Console.WriteLine($"{Subject.State}:Voucher making has started..."); } }
Client Code
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); //Add a subscription subject.Attach(observer1); subject.Attach(observer2); subject.Attach(observer3); //deliver an announcement subject.State = "Starbucks Ten Yuan Vouchers Purchased Successfully"; subject.Notify(); Console.WriteLine("\n\nHappy Ending~"); Console.ReadLine(); }
The results show that
Code Upgrade
Code review found that when notifying observers, execution was sequential, and if one of the observers had carton or an error, it would lead to other observers, so we should use an asynchronous method.
Next we will simulate an increase in voucher making time without affecting other observers.Code directly:
/// <summary> ///abstract observation /// </summary> public abstract class Observer { public abstract Task UpdateAsync(); } /// <summary> ///SMS watcher /// </summary> public class MessageObserver : Observer { public Subject Subject { get; set; } public MessageObserver(Subject subject) { Subject = subject; } public override Task UpdateAsync() { Console.WriteLine($"{Subject.State}:SMS Notification..."); return Task.CompletedTask; } } /// <summary> ///Integral Observer /// </summary> public class BonusObserver : Observer { public Subject Subject { get; set; } public BonusObserver(Subject subject) { Subject = subject; } public override Task UpdateAsync() { Console.WriteLine($"{Subject.State}:Integral increased..."); return Task.CompletedTask; } } /// <summary> ///Voucher watcher /// </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}:Start vouching..."); //Simulate voucher time await Task.Delay(3000); Console.WriteLine($"{Subject.State}:Voucher Completion..."); } } /// <summary> ///Abstract Notifier /// </summary> public abstract class Subject { /// <summary> ///Observer Set /// </summary> protected List<Observer> observers = new List<Observer>(); public string State { get; set; } /// <summary> ///Add an observer /// </summary> /// <param name="observer">observer</param> public void Attach(Observer observer) { observers.Add(observer); } /// <summary> ///Delete observer /// </summary> /// <param name="observer">observer</param> public void Detach(Observer observer) { observers.Remove(observer); } /// <summary> ///Notification /// </summary> /// <returns></returns> public Task Notify() { foreach (var observer in observers) { observer.UpdateAsync(); } return Task.CompletedTask; } }
Client side code:
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); //Add a subscription subject.Attach(observer1); subject.Attach(observer2); subject.Attach(observer3); //deliver an announcement subject.State = "Starbucks Ten Yuan Vouchers Purchased Successfully"; await subject.Notify(); Console.WriteLine("\n\nHappy Ending~"); Console.ReadLine(); }
The results show that:
Delegated Hold Observer Mode
In reality development, it is inappropriate for many observer objects to inherit or implement the same abstract observer together, and the operation method of all observer objects is called an Update(), which does not achieve the desired effect. Therefore, we upgrade the observer mode again and replace the abstract observer with a delegation.
Code directly:
/// <summary> ///SMS watcher /// </summary> public class MessageObserver { public ISubject Subject { get; set; } public MessageObserver(ISubject subject) { Subject = subject; } /// <summary> ///Send SMS /// </summary> /// <returns></returns> public Task SendMessageAsync() { Console.WriteLine($"{Subject.State}:SMS Notification..."); return Task.CompletedTask; } } /// <summary> ///Integral Observer /// </summary> public class BonusObserver { public ISubject Subject { get; set; } public BonusObserver(ISubject subject) { Subject = subject; } /// <summary> ///Add Integral /// </summary> /// <returns></returns> public Task AddBonusAsync() { Console.WriteLine($"{Subject.State}:Integral increased..."); return Task.CompletedTask; } } /// <summary> ///Voucher watcher /// </summary> public class CouponObserver { public ISubject Subject { get; set; } public CouponObserver(ISubject subject) { Subject = subject; } /// <summary> ///Voucher making /// </summary> /// <returns></returns> public async Task MakeCouponAsync() { Console.WriteLine($"{Subject.State}:Start vouching..."); //Simulate voucher time await Task.Delay(3000); Console.WriteLine($"{Subject.State}:Voucher Completion..."); } } /// <summary> ///Abstract Notifier /// </summary> public interface ISubject { /// <summary> ///Notification /// </summary> /// <returns></returns> public Task Notify(); public string State { get; set; } } /// <summary> ///Supports Success Notifiers /// </summary> public class PaySuccessSubject : ISubject { public Func<Task> Update; public string State { get; set; } public Task Notify() { Update(); return Task.CompletedTask; } } }
Client call:
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); //Add a subscription subject.Update += observer1.MakeCouponAsync; subject.Update += observer2.SendMessageAsync; subject.Update += observer3.AddBonusAsync; //deliver an announcement subject.State = "Starbucks Ten Yuan Vouchers Purchased Successfully"; await subject.Notify(); Console.WriteLine("\n\nHappy Ending~"); Console.ReadLine(); } } }
Show the results as above.
Source address: https://gitee.com/sayook/DesignMode