Note that there are some explanatory texts on larger screens.

plurals
  1. POWhy Observer pattern is much more complicated in C# than in Ruby?
    text
    copied!<p>I've read in <a href="http://designpatternsinruby.com/" rel="nofollow noreferrer">"Design Patterns in Ruby" by Russ Olsen</a> how Observer pattern can be implemented in Ruby. I've noticed that Ruby implementation of this pattern is much simpler than C# implementation, e.g. implementation shown in <a href="http://oreilly.com/catalog/9780596527563" rel="nofollow noreferrer">"Programming .NET 3.5" by Jesse Liberty and Alex Horovitz</a>.</p> <p>So I've rewritten "Programming .NET 3.5" Observer pattern example (page 251 of pdf edition) using "Design Patterns in Ruby" algorithm, source code for both implementations can be downloaded from the mentioned websites.</p> <p>Below is the rewritten example, tell me what do you think? <br/> Do we really need to use events and delegates to use the Observer pattern in C#? <br /><br> <br /> </p> <p><strong>Update</strong> After reading comments I would like to ask this question: <br /> Is there any other reason to use delegates and events besides that it makes code shorter? And I don't talk about GUI programming.</p> <p><strong>Update2</strong> I finally got it, delegate is simply a function pointer and event is safer version of delegate which only allows two operations += and -=.</p> <p>My rewrite of "Programming .NET 3.5" example:</p> <pre><code>using System; using System.Collections.Generic; namespace MyObserverPattern { class Program { static void Main() { DateTime now = DateTime.Now; // Create new flights with a departure time and add from and to destinations CarrierSchedule jetBlue = new CarrierSchedule("JetBlue", now); jetBlue.Attach(new AirTrafficControl("Boston")); jetBlue.Attach(new AirTrafficControl("Seattle")); // ATCs will be notified of delays in departure time jetBlue.DepartureDateTime = now.AddHours(1.25); // weather delay jetBlue.DepartureDateTime = now.AddHours(1.75); // weather got worse jetBlue.DepartureDateTime = now.AddHours(0.5); // security delay jetBlue.DepartureDateTime = now.AddHours(0.75); // Seattle puts a ground stop in place // Wait for user //Console.Read(); } } // Subject: This is the thing being watched by Air Traffic Control centers abstract class AirlineSchedule { // properties public string Name { get; set; } public string DeparturnAirport { get; set; } public string ArrivalAirport { get; set; } private DateTime departureDateTime; private List&lt;IATC&gt; observers = new List&lt;IATC&gt;(); public AirlineSchedule(string airline, string outAirport, string inAirport, DateTime leaves ) { this.Name = airline; this.DeparturnAirport = outAirport; this.ArrivalAirport = inAirport; this.DepartureDateTime = leaves; } // Here is where we actually attach our observers (ATCs) public void Attach(IATC atc) { observers.Add(atc); } public void Detach(IATC atc) { observers.Remove(atc); } public void OnChange(AirlineSchedule asched) { if (observers.Count != 0) { foreach (IATC o in observers) o.Update(asched); } } public DateTime DepartureDateTime { get { return departureDateTime; } set { departureDateTime = value; OnChange(this); Console.WriteLine(""); } } }// class AirlineSchedule // A Concrete Subject class CarrierSchedule : AirlineSchedule { // Jesse and Alex only really ever need to fly to one place... public CarrierSchedule(string name, DateTime departing) : base(name, "Boston", "Seattle", departing) { } } // An Observer interface IATC { void Update(AirlineSchedule sender); } // The Concrete Observer class AirTrafficControl : IATC { public string Name { get; set; } public AirTrafficControl(string name) { this.Name = name; } public void Update(AirlineSchedule sender) { Console.WriteLine( "{0} Air Traffic Control Notified:\n {1}'s flight 497 from {2} " + "to {3} new deprture time: {4:hh:mmtt}", Name, sender.Name, sender.DeparturnAirport, sender.ArrivalAirport, sender.DepartureDateTime ); Console.WriteLine("---------"); } } } </code></pre> <p>Here is mentioned Ruby code:</p> <pre><code>module Subject def initialize @observers=[] end def add_observer(observer) @observers &lt;&lt; observer end def delete_observer(observer) @observers.delete(observer) end def notify_observers @observers.each do |observer| observer.update(self) end end end class Employee include Subject attr_reader :name, :address attr_reader :salary def initialize( name, title, salary) super() @name = name @title = title @salary = salary end def salary=(new_salary) @salary = new_salary notify_observers end end class TaxMan def update( changed_employee ) puts("Send #{changed_employee.name} a new tax bill!") end end fred = Employee.new('Fred', 'Crane Operator', 30000.0) tax_man = TaxMan.new fred.add_observer(tax_man) </code></pre> <p>Here is "Programming .NET 3.5" example that I rewrote:</p> <pre><code>using System; namespace Observer { class Program { static void Main() { DateTime now = DateTime.Now; // Create new flights with a departure time and add from and to destinations CarrierSchedule jetBlue = new CarrierSchedule("JetBlue", now); jetBlue.Attach(new AirTrafficControl("Boston")); jetBlue.Attach(new AirTrafficControl("Seattle")); // ATCs will be notified of delays in departure time jetBlue.DepartureDateTime = now.AddHours(1.25); // weather delay jetBlue.DepartureDateTime = now.AddHours(1.75); // weather got worse jetBlue.DepartureDateTime = now.AddHours(0.5); // security delay jetBlue.DepartureDateTime = now.AddHours(0.75); // Seattle puts a ground stop in place // Wait for user Console.Read(); } } // Generic delegate type for hooking up flight schedule requests public delegate void ChangeEventHandler&lt;T,U&gt; (T sender, U eventArgs); // Customize event arguments to fit the activity public class ChangeEventArgs : EventArgs { public ChangeEventArgs(string name, string outAirport, string inAirport, DateTime leaves) { this.Airline = name; this.DeparturnAirport = outAirport; this.ArrivalAirport = inAirport; this.DepartureDateTime = leaves; } // Our Properties public string Airline { get; set; } public string DeparturnAirport { get; set; } public string ArrivalAirport { get; set; } public DateTime DepartureDateTime { get; set; } } // Subject: This is the thing being watched by Air Traffic Control centers abstract class AirlineSchedule { // properties public string Name { get; set; } public string DeparturnAirport { get; set; } public string ArrivalAirport { get; set; } private DateTime departureDateTime; public AirlineSchedule(string airline, string outAirport, string inAirport, DateTime leaves) { this.Name = airline; this.DeparturnAirport = outAirport; this.ArrivalAirport = inAirport; this.DepartureDateTime = leaves; } // Event public event ChangeEventHandler&lt;AirlineSchedule, ChangeEventArgs&gt; Change; // Invoke the Change event public virtual void OnChange(ChangeEventArgs e) { if (Change != null) { Change(this, e); } } // Here is where we actually attach our observers (ATCs) public void Attach(AirTrafficControl airTrafficControl) { Change += new ChangeEventHandler&lt;AirlineSchedule, ChangeEventArgs&gt; (airTrafficControl.Update); } public void Detach(AirTrafficControl airTrafficControl) { Change -= new ChangeEventHandler&lt;AirlineSchedule, ChangeEventArgs&gt; (airTrafficControl.Update); } public DateTime DepartureDateTime { get { return departureDateTime; } set { departureDateTime = value; OnChange(new ChangeEventArgs( this.Name, this.DeparturnAirport, this.ArrivalAirport, this.departureDateTime)); Console.WriteLine(""); } } } // A Concrete Subject class CarrierSchedule : AirlineSchedule { // Jesse and Alex only really ever need to fly to one place... public CarrierSchedule(string name, DateTime departing): base(name,"Boston", "Seattle", departing) { } } // An Observer interface IATC { void Update(AirlineSchedule sender, ChangeEventArgs e); } // The Concrete Observer class AirTrafficControl : IATC { public string Name { get; set; } // Constructor public AirTrafficControl(string name) { this.Name = name; } public void Update(AirlineSchedule sender, ChangeEventArgs e) { Console.WriteLine( "{0} Air Traffic Control Notified:\n {1}'s flight 497 from {2} " + "to {3} new deprture time: {4:hh:mmtt}", Name, e.Airline, e.DeparturnAirport, e.ArrivalAirport, e.DepartureDateTime); Console.WriteLine("---------"); } public CarrierSchedule CarrierSchedule { get; set; } } } </code></pre>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload