Continuing our comparison of design patterns in Ruby and C#, we’re taking a look at the Observer pattern. With this pattern, we have a subject and a list of observers that are interested in knowing when changes occur on the subject. This happens in a push model, the subject maintains the list observers and notifies each when needed.

In our examples, we’re going to create a Vehicle object that contains a couple of key properties, Mileage and MileageAtLastOilChange. When a new Vehicle instance is created both properties start at zero. As the vehicle’s Drive() method is called and miles are added to it, it notifies any known observers. The MilesUntilNextOilChange property lets others know how long the vehicle can be driven until it should get an oil change. When the PerformOilChangeMethod() is called, the MileageAtLastOilChange property is set to the current mileage and registered observers are notified that changes occurred.

We are also going to create a Dashboard object that will let us know how many miles the vehicle can be driven until it needs it’s next oil change as well as a warning message alerting the driver that it needs a oil change when it reaches 3000 miles.

C# Example

First we’ll create our Vehicle object as well as an IVehicleObserver interface that must be implemented by any Vehicle observers. Note that the Vehicle object contains an AddObserver() that allows observers to register themselves with the Vehicle.

 
using System.Collections.Generic;

namespace DesignPatterns.ObserverPattern
{
    public class Vehicle
    {
        private IList<IVehicleObserver> _observers = new List<IVehicleObserver>();

        public int Mileage { get; private set; }
        public int MileageAtLastOilChange { get; private set; }
        public int MilesUntilNextOilChange { get { return 3000 - (Mileage - MileageAtLastOilChange); } }

        public Vehicle()
        {
            Mileage = 0;
            MileageAtLastOilChange = 0;
        }

        public void Drive(int miles)
        {
            Mileage += miles;
            NotifyObservers();
        }

        public void PerformOilChange()
        {
            MileageAtLastOilChange = Mileage;
            NotifyObservers();
        }

        public void AddObserver(IVehicleObserver observer)
        {
            _observers.Add(observer);
        }

        private void NotifyObservers()
        {
            foreach (var observer in _observers)
                observer.Notify(this);
        }
    }
}
 
namespace DesignPatterns.ObserverPattern
{
    public interface IVehicleObserver
    {
        void Notify(Vehicle vehicle);
    }
}

And now we’ll create our Dashboard class which will implement the IVehicleObserver interface. When the Notify() method is called, it updates the Message property to reflect the vehicle’s current status.

 
namespace DesignPatterns.ObserverPattern
{
    public class Dashboard : IVehicleObserver
    {
        public string Message { get; private set; }

        public Dashboard()
        {
            Message = string.Empty;
        }

        public void Notify(Vehicle vehicle)
        {
            Message = vehicle.MilesUntilNextOilChange <= 0
                      ? "Time for an oil change!"
                      : string.Format("Next oil change is due in {0} miles.", vehicle.MilesUntilNextOilChange);
        }
    }
}
&#91;/source&#93;

<p>Let’s take a look at how these two will work together.</p>

var dashboard = new Dashboard(); var vehicle = new Vehicle(); vehicle.AddObserver(dashboard); vehicle.Drive(2000); Console.WriteLine(dashboard.Message); vehicle.Drive(1500); Console.WriteLine(dashboard.Message); vehicle.PerformOilChange(); Console.WriteLine(dashboard.Message); vehicle.Drive(1000); Console.WriteLine(dashboard.Message);

Ruby Example

The Ruby example really isn’t that much unlike the C# version. The big difference is that we don’t need to create an IVehicleObserver interface, we just need to make sure our Dashboard object has a “notify” method that accepts a Vehicle object.

 
class Vehicle
  attr_reader :mileage, :mileage_at_last_oil_change

  def initialize
    @mileage = 0
    @mileage_at_last_oil_change = 0
    @observers = []
  end

  def drive(miles)
    @mileage += miles
    notify_observers
  end

  def miles_until_next_oil_change
    3000 - (@mileage - @mileage_at_last_oil_change)
  end

  def perform_oil_change
    @mileage_at_last_oil_change = @mileage
    notify_observers
  end

  def add_observer(observer)
    @observers << observer
  end

  private

  def notify_observers
    @observers.each {|observer| observer.notify self}
  end
end
&#91;/source&#93;
&#91;/fusion_builder_column&#93;&#91;fusion_builder_column row_column_index="5_6" type="1_1" background_position="left top" background_color="" border_size="" border_color="" border_style="solid" spacing="yes" background_image="" background_repeat="no-repeat" padding="" margin_top="0px" margin_bottom="0px" class="" id="" animation_type="" animation_speed="0.3" animation_direction="left" hide_on_mobile="no" center_content="no" min_height="none"&#93;&#91;source language='ruby'&#93; 
class Dashboard
  attr_reader :message

  def initialize
    @message = ""
  end

  def notify(vehicle)
    @message = vehicle.miles_until_next_oil_change <= 0 ?
                "Time for an oil change!" :
                "Next oil change is due in #{vehicle.miles_until_next_oil_change} miles."
  end
end
&#91;/source&#93;
<p>And using the objects is very similiar to the C# example as well.</p>
require "dashboard" require "vehicle" dashboard = Dashboard.new vehicle = Vehicle.new vehicle.add_observer dashboard vehicle.drive 2000 puts dashboard.message vehicle.drive 1500 puts dashboard.message vehicle.perform_oil_change puts dashboard.message vehicle.drive 1000 puts dashboard.message

Simplifying Our Objects

Both languages have built-in mechanisms that we can take advantage of that can make implementing the Observer pattern easy.

In .Net, we’re given out-of-the-box event handling. We just need to create a delegate that defines the method our observers will use for receiving notifications. This reduces the need for an IVehicleObserver interface and simplifies our design.

 
public delegate void MilesChangedHandler(Vehicle vehicle);
 
public class Vehicle
{
    public int Mileage { get; private set; }
    public int MileageAtLastOilChange { get; private set; }
    public int MilesUntilNextOilChange { get { return 3000 - (Mileage - MileageAtLastOilChange); } }
    public event MilesChangedHandler MilesChangedEvent;

    public Vehicle()
    {
        Mileage = 0;
        MileageAtLastOilChange = 0;
    }

    public void Drive(int miles)
    {
        Mileage += miles;
        NotifyObservers();
    }

    public void PerformOilChange()
    {
        MileageAtLastOilChange = Mileage;
        NotifyObservers();
    }

    private void NotifyObservers()
    {
        if (MilesChangedEvent != null)
            MilesChangedEvent(this);
    }
}

And no changes have to be made to our Dashboard object.

 
public class Dashboard : IVehicleObserver
{
    public string Message { get; private set; }

    public Dashboard()
    {
        Message = string.Empty;
    }

    public void Notify(Vehicle vehicle)
    {
        Message = vehicle.MilesUntilNextOilChange <= 0
                             ? "Time for an oil change!"
                             : string.Format("Next oil change is due in {0} miles.", vehicle.MilesUntilNextOilChange);
    }
}
&#91;/source&#93;
<p>Using the classes changes a bit, note how we subscribe to event on the Vehicle object.</p>
var dashboard = new Dashboard(); var vehicle = new Vehicle(); vehicle.MilesChangedEvent += dashboard.Notify; vehicle.Drive(2000); Console.WriteLine(dashboard.Message); vehicle.Drive(1500); Console.WriteLine(dashboard.Message); vehicle.PerformOilChange(); Console.WriteLine(dashboard.Message); vehicle.Drive(1000); Console.WriteLine(dashboard.Message);

Ruby also has some built goodness to make our work a little easier. We are actually given an Observable module that we can add to our Vehicle class that will handle the adding, removing, and notifying of observers. The only minor addition we need to add to our object is the a call to the “changed” method when we want to indicate that our object’s state has changed.

 
require "observer"

class Vehicle
  include Observable
  
  attr_reader :mileage, :mileage_at_last_oil_change

  def initialize
    @mileage = 0
    @mileage_at_last_oil_change = 0
    @observers = []
  end

  def drive(miles)
    @mileage += miles
    changed
    notify_observers self
  end

  def miles_until_next_oil_change
    3000 - (@mileage - @mileage_at_last_oil_change)
  end

  def perform_oil_change
    @mileage_at_last_oil_change = @mileage
    changed
    notify_observers self
  end
end

To use the module, our observers have to have an “update” method so we’ll need to rename our “notify” method on the Dashboard object.

 
class Dashboard
  attr_reader :message

  def initialize
    @message = ""
  end

  def update(vehicle)
    @message = vehicle.miles_until_next_oil_change <= 0 ?
                "Time for an oil change!" :
                "Next oil change is due in #{vehicle.miles_until_next_oil_change} miles."
  end
end
&#91;/source&#93;
<p>And our usage stays exactly the same!</p>
require "dashboard" require "vehicle" dashboard = Dashboard.new vehicle = Vehicle.new vehicle.add_observer dashboard vehicle.drive 2000 puts dashboard.message vehicle.drive 1500 puts dashboard.message vehicle.perform_oil_change puts dashboard.message vehicle.drive 1000 puts dashboard.message

That’s it for the Observer pattern, next time we’ll take a look at the Composite pattern.

kick it on DotNetKicks.com