Speaking of observer patterns, it is estimated that a heap can be found in the garden. So the purpose of writing this blog is two points:
1. The observer pattern is the necessary mode of writing loose coupling code, the importance is self-evident, aside from the code level, many components have adopted the Publish-subscribe mode, so I want to follow my own understanding to redesign a usage scene and the Observer pattern flexibly used in it
2. I want to make a summary of the three scenarios in C # that implement the observer pattern, and I don't see this summary yet.
Now let's assume a scenario like this and use the observer pattern to implement the requirements:
Smart home in the future into each household, each home has an API for customers to customize the integration, so the first smart alarm Clock (Smartclock) debut, manufacturers for this alarm clock provides a set of APIs, when set an alarm time after the alarm clock will be notified at this point, our intelligent milk heater, Bread baking machine, squeeze toothpaste equipment to subscribe to this alarm clock message, automatically prepared for the owner of milk, bread, toothpaste and so on.
This scene is very typical of the observer pattern, the alarm clock is a theme (subject), the milk heater, the bread baking machine, the squeeze toothpaste equipment is the observer (Observer), they only need to subscribe to this topic can be implemented loosely coupled coding model. Let's implement this requirement one by one in three different scenarios.
First, using the. NET event model to implement
The event model in. NET is a typical observer pattern that is heavily used in code after. NET birth, and we see how the incident model is used in this scenario.
First introduce the smart alarm clock, manufacturers provide a very simple set of APIs
public void Setalarmtime (TimeSpan TimeSpan) { _alarmtime = _now (). ADD (TimeSpan); Runbackgourndrunner (_now, _alarmtime); }
Setalarmtime (TimeSpan TimeSpan) is used for timing, when the user sets good one time, the alarm will run in the background a similar to while (true) cycle comparison time, when the alarm time to send a notification event come out
protected void Runbackgourndrunner (func<datetime> now,datetime? alarmtime) { if (alarmtime.hasvalue) { var canceltoken = new CancellationTokenSource (); var task = new Task (() = {( !canceltoken.iscancellationrequested) { if (now). Areequals (Alarmtime.value)) { //alarm time to itistimetoalarm (); Canceltoken.cancel (); } CancelToken.Token.WaitHandle.WaitOne (Timespan.fromseconds (2)); } , Canceltoken.token, taskcreationoptions.longrunning); Task. Start (); } }
Other code is not important, focus on when the alarm time to execute itistimetoalarm (); Here we issue an event to inform Subscribers that the. NET implementation of the event model has three elements,
1. For the theme (subject) to define an event, public event Action<clock, alarmeventargs> Alarm;
2. Define a EventArgs, or AlarmEventArgs, for the information of the subject (subject), which contains all the information about the event.
3. Topics (subject) issue events in the following ways
var args = new AlarmEventArgs (_alarmtime.value, 0.92m); Onalarmevent (args);
Definition of the Onalarmevent method
public virtual void Onalarm (AlarmEventArgs e) { if (alarm!=null) Alarm (this,e); }
Note the naming, event content-alarmeventargs, event-alarm (verbs such as keypress), the method that triggers the event, void Onalarm (), which conform to the naming conventions of the event model.
The Smart Alarm Clock (smartclock) has been implemented and we subscribe to this alarm message in the milk heater (milkschedule):
public void preparemilkinthemorning () { _clock. Alarm + = (clock, args) = { Message = "prepraring Milk for the owner, the time is {0}, the electric quantit Y is {1}% ". Formatwith ( args. AlarmTime, args. ELECTRICQUANTITY*100); Console.WriteLine (Message); }; _clock. Setalarmtime (Timespan.fromseconds (2)); }
_clock can also be used in bread baking machines. alarm+= (Clock,args) =>{//it is time to roast bread} subscribes to the alarm message.
At this point, the event model is complete, the implementation process is a bit cumbersome, and the use of the event model will have memory leak problem, when the Observer (Obsever) subscribed to a long life cycle of the topic (the topic life cycle longer than the observer), the observer will not be memory recycling ( Because there is also a reference to the subject), see Understanding and avoiding Memory Leaks with event handlers and event aggregators, developers need to display unsubscribe from the topic (-=).
Old A in the garden also wrote a blog about how to use weak references to solve the problem: how to solve the memory leak problem caused by the event, the Weak event handlers.
Second, using the Iobservable<out t> and Iobserver<in t> in. NET to implement the Observer pattern
Iobservable<out t> As the name implies-observable, that is, the subject (subject), observer is obviously the observer.
In our scene, the smart alarm clock is iobservable, which defines only one method IDisposable Subscribe (iobserver<t> observer); The method is named a bit dizzy, Subscribe is the meaning of the subscription, which differs from the previously mentioned (Observer) subscription topic (subject). Here is the topic (subject) to subscribe to the Observer (Observer), in fact, it makes sense, because under the model, the subject (subject) maintains a list of observers (Observer), so there is a topic to subscribe to the Observer said, Let's look at the IDisposable Subscribe (iobserver<t> observer) of the alarm clock to achieve:
Public IDisposable Subscribe (iobserver<alarmdata> observer) { if (!_observers. Contains (Observer)) { _observers. ADD (Observer); } return new Disposedaction (() = _observers. REMOVE (Observer)); }
You can see that a list of observers is maintained here _observers, and the alarm goes through all the Observer lists to notify the Observer
public override void Itistimetoalarm () { var alarm = new Alarmdata (_alarmtime.value, 0.92m); _observers. ForEach (O=>o.onnext (alarm)); }
Obviously, the Observer has a OnNext method, the method signature is a alarmdata, represents the message data to be notified, next look at the realization of the milk heater, the milk heater as the Observer (Observer) of course to achieve IObserver interface
Public void Subscribe (TimeSpan TimeSpan) { _unsubscriber = _clock. Subscribe (this); _clock. Setalarmtime (TimeSpan); } Public void Unsubscribe () { _unsubscriber.dispose (); } public void OnNext (alarmdata value) { Message = ' prepraring milk for the owner ', the time is {0}, the Electr IC quantity is {1}% ". Formatwith ( value. AlarmTime, value. Electricquantity *); Console.WriteLine (Message); }
In addition, to facilitate the use of bread roaster, we added two methods subscribe () and unsubscribe () to see the call process
var milkschedule = new Milkschedule ();//actmilkschedule.subscribe (Timespan.fromseconds (12));
Three, Action function type scheme
Before I introduce this scenario, I need to state that the scheme is not an observer model, but it can do the same thing, and it's more concise and it's my favorite usage.
In this scenario, the API provided by the smart Alarm Clock (smartclock) needs to be designed like this:
public void Setalarmtime (TimeSpan timespan,action<alarmdata> alarmaction) { _alarmtime = _now (). ADD (TimeSpan); _alarmaction = alarmaction; Runbackgourndrunner (_now, _alarmtime); }
A action<t> is accepted in the method signature, and the alarm is executed directly after the action<t>:
public override void Itistimetoalarm () { if (_alarmaction! = null) { var alarmdata = new Alarmdata (_ Alarmtime.value, 0.92m); _alarmaction (Alarmdata); } }
The use of this API in milk heaters is also simple:
_clock. Setalarmtime (Timespan.fromseconds (1), (data) = { Message = "prepraring Milk for the owner, and the time is {0 }, the electric quantity is {1}% ". Formatwith ( data. AlarmTime, data. Electricquantity *); });
In the actual use of the process I will design this API into a fluent model, calling up the code clearer:
API in Smart Alarm Clock (smartclock):
Public Clock Setalarmtime (TimeSpan TimeSpan) { _alarmtime = _now (). ADD (TimeSpan); Runbackgourndrunner (_now, _alarmtime); return this; } public void Onalarm (action<alarmdata> alarmaction) { _alarmaction = alarmaction; }
Call in the milk heater:
_clock. Setalarmtime (Timespan.fromseconds (2)) . Onalarm (data) = { Message = "prepraring Milk for the owner, the time was {0}, the electric quantity is {1}% ". Formatwith ( data. AlarmTime, data. Electricquantity *); });
Obviously, the improved syntax is better: alarm clock. Set the time of the alarms (). When the Alarm (() =>{performs the following functions})
This functional style is more concise, but there are obvious shortcomings, the model does not support multiple observers, when the bread baking machine using such an API, will cover the function of the milk heater, that is, each time only one observer to support the use.
In conclusion, this paper summarizes the three observer model implementations under. NET, and it is our ultimate goal to choose the most suitable model under the programming scenario.