3 ways to implement the observer pattern in C #

Source: Internet
Author: User
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.

  • Contact Us

    The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

    If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

    A Free Trial That Lets You Build Big!

    Start building with 50+ products and up to 12 months usage for Elastic Compute Service

    • Sales Support

      1 on 1 presale consultation

    • After-Sales Support

      24/7 Technical Support 6 Free Tickets per Quarter Faster Response

    • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.