rob*_*t_d 4 c# ruby observer-pattern
我读过Russ Olsen的"Ruby模式设计", 如何在Ruby中实现Observer模式.我注意到这种模式的Ruby实现比C#实现简单得多,例如Jesse Liberty和Alex Horovitz在"Programming .NET 3.5"中显示的实现.
因此,我使用"Ruby中的设计模式"算法重写了"Programming .NET 3.5"Observer模式示例(pdf版本的第251页),两种实现的源代码都可以从上述网站下载.
下面是重写的例子,告诉我你的想法是什么?
我们真的需要使用事件和委托来使用C#中的Observer模式吗?
更新
阅读评论后,我想问一下这个问题:
除了使代码缩短之外,还有其他理由使用委托和事件吗?我不谈论GUI编程.
Update2 我终于明白了,委托只是一个函数指针,事件是委托的更安全版本,它只允许两个操作+ =和 - =.
我重写了"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}'s flight 497 from {2} " +
"to {3} new deprture time: {4:hh:mmtt}",
Name,
sender.Name,
sender.DeparturnAirport,
sender.ArrivalAirport,
sender.DepartureDateTime );
Console.WriteLine("---------");
}
}
}
Run Code Online (Sandbox Code Playgroud)
这里提到Ruby代码:
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)
Run Code Online (Sandbox Code Playgroud)
这里是我编写的"Programming .NET 3.5"示例:
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}'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; }
}
}
Run Code Online (Sandbox Code Playgroud)
Tom*_*cek 13
设计模式表达一般意义上的想法,而不是应该用于实现模式的特定类层次结构.在C#中,您不会使用类和接口(例如在Java中)实现该想法,因为它提供了更直接的解决方案.您可以改用事件和代理.这是一篇很好的文章,您可以查看:
请注意,观察者不是唯一可以在C#中更优雅地编码的模式.例如,策略模式可以使用C#中的(单行)lambda表达式实现:
也就是说,我对设计模式在许多方面持怀疑态度,但它们可能有用作参考.但是,不应盲目使用它们.一些作者可能认为严格遵循模式是编写高质量"企业"软件的唯一方法,但事实并非如此!
编辑 这是您的Ruby代码的简洁版本.我没有阅读C#版本,因为它太复杂了(我甚至会说混淆):
class Employee {
public Employee(string name, string address, int salary) {
Name = name; Address = address; this.salary = salary;
}
private int salary;
public event Action<Employee> SalaryChanged;
public string Name { get; set; }
public string Address { get; set; }
public int Salary {
get { return salary; }
set {
salary = value;
if (SalaryChanged != null) SalaryChanged(this);
}
}
var fred = new Employee(...);
fred.SalaryChanged += (changed_employee) =>
Console.WriteLine("Send {0} a new tax bill!", changed_employee.Name);
Run Code Online (Sandbox Code Playgroud)
这是对活动和代表的完美使用.C#3.0 lambda函数使你的例子比Ruby更简单:-).
我没有这本书,所以我无法证实这一点,但是这个例子使用事件和委托的原因很可能是因为它们是C#语言中的第一类构造.从本质上讲,C#已经为您实现了Observer模式,因此您可以在任何地方使用它.
此外,我怀疑C#示例笨重的部分原因是因为杰西自由并没有让我成为一个非常娴熟的作家.他的一些书有点太公式化和死记硬背(例如"在Y小时学习编程语言X!").结果就是你结束了尴尬的,有些匆忙的例子,看起来就像没有编译错误就从他的IDE中复制粘贴了.