I read in “Designing Templates in Ruby” by Russ Olsen how an Observer pattern can be implemented in Ruby. I noticed that the Ruby implementation of this template is much simpler than the C # implementation, for example, the implementation is shown in ".NET 3.5 Programming" by Jesse Liberty and Alex Horowitz .
So, I rewrote an example of the “Programming .NET 3.5” template (p. 251 in pdf format) using the “Design Patterns in Ruby” algorithm, the source code for both implementations can be downloaded from these sites.
Below is a rewritten example, tell me what you think?
Do we really need to use events and delegates to use the Observer pattern in C #?
Update After reading the comments, I would like to ask this question:
Is there any other reason to use delegates and events besides the fact that it makes the code shorter? And I'm not talking about GUI programming.
Update2 I finally got it, the delegate is just a pointer to a function, and the event is a safer version of the delegate that allows only two operations: + = and - =.
My version of "Programming .NET 3.5":
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<IATC> observers = new List<IATC>(); 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} flight 497 from {2} " + "to {3} new deprture time: {4:hh:mmtt}", Name, sender.Name, sender.DeparturnAirport, sender.ArrivalAirport, sender.DepartureDateTime ); Console.WriteLine("---------"); } } }
Ruby code is mentioned here:
module Subject def initialize @observers=[] end def add_observer(observer) @observers << 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)
Here is an example of "Programming.NET 3.5" that I rewrote:
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<T,U> (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<AirlineSchedule, ChangeEventArgs> 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<AirlineSchedule, ChangeEventArgs> (airTrafficControl.Update); } public void Detach(AirTrafficControl airTrafficControl) { Change -= new ChangeEventHandler<AirlineSchedule, ChangeEventArgs> (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} 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; } } }