Page 1 of 1

The Observer pattern and Coroutines.

#1 JacobH  Icon User is offline

  • D.I.C Head
  • member icon

Reputation: 26
  • View blog
  • Posts: 176
  • Joined: 07-September 15

Posted 10 June 2016 - 11:19 PM

Hello there fellow programmers,

Today I am going to be writing a little bit about the the observer pattern. In our example we will use interfaces and events to implement the pattern. In addition to that, there is something called coroutines, and they are a neat way to make use of the IEnumerator system in C# which I think is underused. So for this article, we will implement a coroutine into the observer pattern example.


The observer pattern - what is it?


The observer pattern is really just a strategy that allows an objects state to be tracked by other objects. The idea is to create one-to-many dependency between objects where a state change in one object results in all its dependents being notified and updated automatically.

Think of it like this:

Observable observable = new Observable();
Observer observer = new Observer();
observer.Observe(observable);
// On update, observers will detect the "Hello" message.
observable.Update("Hello");




Implementing the observer pattern in C# with events and Interfaces.


When it comes to implementing the observer pattern, C# really has made it very convenient with their events system. So lets start with the basics. We know we need an observable object, and an observer. Lets make an interface to define those two objects, and a class that will represent the data being observed. In our case we will use EventArgs for the data, as that is very familiar to C# users.

In order, we have:

1. The data class that holds the data we want to be observed, using the EventArgs that C# offers.
2. The interface that defines the observed object, which contains an event handler that passes the data above when raised.
3. The interface that defines the observer object, which can observe the above object.

    // Defines the data being observed in this case.
    public class MessageArgs : EventArgs
    {
        public MessageArgs(string message, string reason)
        {
            Message = message;
            Reason = reason;
        }

        public string Message { get; }
        public string Reason { get; }
    }

    // Defines how a message can be observable.
    public interface IMessageObservable
    {
        event EventHandler<MessageArgs> MessageReceived;
    }
    
    // Defines a message observer.
    public interface IMessageObserver
    {
        void Observe(IMessageObservable observable);
    }



Now, we will want to implement our interfaces we wrote. First, lets define the Observer.

    // This is the observer implementation.
    public class ObservableMessage : IMessageObservable
    {
        public event EventHandler<MessageArgs> MessageReceived;

        // This function allows worker classes to write something that can be signaled to observers.
        public void SendMessage(string message, string reason)
        {
             // Here we can perform some tests before sending the message
            if (string.IsNullOrEmpty(reason))
            {
                throw new ArgumentNullException(nameof(reason));
            }

            if (MessageReceived == null)
            {
                return;
            }

            var printMessageArgs = new MessageArgs(message, reason);

            OnMessageReceived(printMessageArgs);
        }

        protected virtual void OnMessageReceived(MessageArgs e)
        {
            // This raises our event observers listen to.
            MessageReceived?.Invoke(this, e);
        }

        public override string ToString()
        {
            // We use this just for the example to print the 'sender'.
            return "Observable Message.";
        }
    }



And of course, we need to implement the observer.

    // This is the observer implementation.
    public class MessageObserver : IMessageObserver
    {
        public void Observe(IMessageObservable observable)
        {
            observable.MessageReceived += PrinterOnMessageReceived;
        }

        private static void PrinterOnMessageReceived(object sender, MessageArgs messageArgs)
        {
            Console.WriteLine($"Sender: {sender}{Environment.NewLine}" +
                              $"Message: {messageArgs.Message}{Environment.NewLine}" +
                              $"Reason: {messageArgs.Reason}{Environment.NewLine} ");
        }
    }
}



Coroutine's - what are they?

Before reading on, keep in mind the observer example above is valid regardless of the coroutine used in this tutorial. I chose to show the concept of coroutines in this tutorial purley because I think they are neat and a great way to learn about and make use the IEnumerator system.

Google describes a coroutine as this:

Quote

Coroutines are a general control structure whereby flow control is cooperatively passed between two different routines without returning.


To be honest, wiki describes the concept much better than I can. https://en.wikipedia.../wiki/Coroutine

So this is our coroutine class we will use. I include the DateTime extension method for the WaitFor method you see below.

    public static class DateTimeExtensions
    {
        public static long MSecToNow(this DateTime date)
        {
            return (DateTime.Now.Ticks - date.Ticks) / TimeSpan.TicksPerMillisecond;
        }
    }

    public abstract class Coroutine
    {
        private IEnumerator _enumerator;
        private bool _started;
        private bool _completed;

        public virtual void Start()
        {
            _started = true;
            _enumerator = OnUpdate();
        }

        public virtual bool Update()
        {
            if (!_started)
                Start();

            var result = Process(_enumerator);

            if (result || _completed)
                return result;

            _completed = true;
            OnComplete();

            return false;
        }


        protected abstract IEnumerator OnUpdate();

        protected virtual void OnComplete()
        {

        }

        protected IEnumerator WaitFor(int waitTimeMs)
        {
            var startedWaiting = DateTime.Now;
            while (startedWaiting.MSecToNow() < waitTimeMs)
            {
                yield return true;
            }
        }

        private static bool Process(IEnumerator enumerator)
        {
            bool result;
            if (enumerator == null) return false;
            var subEnumerator = enumerator.Current as IEnumerator;
            if (subEnumerator != null)
            {
                result = Process(subEnumerator);
                if (!result)
                {
                    result = enumerator.MoveNext();
                }
            }
            else
            {
                result = enumerator.MoveNext();
            }

            return result;
        }
    }



Once again, we need to implement our class above. In our example the OnUpdate method will work with the PassedControl method in order to complete a series of actions. In our case, those actions are to wait for some period of time and then have our observer raise events for our observers to detect.

    public class MessageCoroutine : Coroutine
    {
        protected IEnumerator PassedControl(ObservableMessage observer)
        {
            observer.SendMessage("Hello", "Testing coroutines. Waiting one second.");
            yield return WaitFor(1000);
            Console.WriteLine("Passed Control Complete");
        }

        protected override IEnumerator OnUpdate()
        {
            // Register our observer.
            var messagePrinter = new ObservableMessage();            
            var messagePrinterObservers = new MessageObserver();
            messagePrinterObservers.Observe(messagePrinter);
            // Use our IEnumerator to raise the event being observed.
            yield return PassedControl(messagePrinter);
            // Now do some work here.
            Console.WriteLine("Waiting one second from the main OnUpdate now.");
            yield return WaitFor(1000);
            messagePrinter.SendMessage("Updating", "Updating the test to wait another 2 seconds.");
            yield return WaitFor(2000);
            messagePrinter.SendMessage("Bye", "The test has been complete.");
        }
    }



Tying it all together

So lets combine everything from above and run our little example demo to ensure it works. Since the coroutines have the possibility to get recursive if not handled properly, we use a stop watch in our example to create a time out limit.
    public static class Program
    {
        public static void Main(string[] args)
        {
            RunCoroutine(10000);
            Console.ReadLine();
        }

        private static void RunCoroutine(int timeOutMs)
        {
            var coroutine = new MessageCoroutine();

            var working = true;
            var stopWatch = Stopwatch.StartNew();

            while (working)
            {
                if (stopWatch.ElapsedMilliseconds >= timeOutMs)
                {
                    stopWatch.Stop();
                    throw new TimeoutException($"The coroutine timed out. Time elapsed {stopWatch.ElapsedMilliseconds}.");
                }
                working = coroutine.Update();
            }

            stopWatch.Stop();
        }
    }



The above code should produce this output:

Sender: Observable Message.
Message: Hello
Reason: Testing coroutines. Waiting one second.
 
Passed Control Complete
Waiting one second from the main OnUpdate now.

Sender: Observable Message.
Message: Updating
Reason: Updating the test to wait another 2 seconds.
 
Sender: Observable Message.
Message: Bye
Reason: The test has been complete.



The files used in this tutorial are included for completness sake. That is about it. Today hopefully you learned a little bit about how programming with cooperation in mind can be very useful.File

Is This A Good Question/Topic? 2
  • +

Replies To: The Observer pattern and Coroutines.

#2 Recoil  Icon User is offline

  • D.I.C Addict

Reputation: 51
  • View blog
  • Posts: 504
  • Joined: 28-June 08

Posted 11 June 2016 - 07:56 AM

I really liked this breakdown of the Observer Pattern. However, it may be helpful for some to list instances where this pattern can be used, or instances where this pattern is most likely used.

I have been reading a lot about this pattern myself the last week, but mostly all C++ examples which (for me) aren't the easiest to convert to something I can use.

Great article!
Was This Post Helpful? 0
  • +
  • -

#3 andrewsw  Icon User is offline

  • say what now
  • member icon

Reputation: 6403
  • View blog
  • Posts: 25,878
  • Joined: 12-December 12

Posted 11 June 2016 - 08:13 AM

These useful references provide slightly different examples:

Observer Design Pattern - an Auctioneer (site uses Java IIRC)
Observer Pattern - News Agency (Java as well I believe)
Observer - Investors and Stock

Java/C# isn't significant when discussing the pattern.
Was This Post Helpful? 1
  • +
  • -

#4 JacobH  Icon User is offline

  • D.I.C Head
  • member icon

Reputation: 26
  • View blog
  • Posts: 176
  • Joined: 07-September 15

Posted 20 June 2016 - 07:44 PM

@Recoil Thanks for the compliment and advice. I think listing some common problems that the observer pattern can solve quiet nicely is something I will add soon. Past that I think it is important to let individuals who are learning to program (hint: that is every programmer, for life ;)) figure out what works for them.

There is more than one right answer to many problems in the programming world. In the end, I think great program designs come from a mixture of trial and error, raw knowledge/know-how, and a bit of personal touches.

@andrewsw Great links. You can tell just from the text alone that I reviewed some of them to write this "tutorial". I focused on the C# aspect of it as I think C# makes implementing a lot of strong design patterns incredibly easy.
Was This Post Helpful? 1
  • +
  • -

#5 andrewsw  Icon User is offline

  • say what now
  • member icon

Reputation: 6403
  • View blog
  • Posts: 25,878
  • Joined: 12-December 12

Posted 20 June 2016 - 11:52 PM

I did that recently myself, took a Java example and "improved" it with C#. Must be quite common ;)
Was This Post Helpful? 0
  • +
  • -

Page 1 of 1