把委托说透(4):委托与设计模式
委托与很多设计模式都有着千丝万缕的联系,在前面的随笔中已经介绍了委托与策略模式的联系,本节主要来讨论委托与其他两个模式:观察者模式和模板方法模式。
委托与观察者模式
在.NET中,很多设计模式得到了广泛应用,如foreach关键字实现了迭代器模式。同样的,.NET中也内置了观察者模式的实现方式,这种方式就是委托。
观察者模式的一般实现
网上可以找到很多资料介绍观察者模式的实现,我这里介绍一种简单的退化后的观察者模式,即Subject类为具体类,在其之上不再进行抽象。
public class Subject { private List<Observer> observers = new List<Observer>(); private string state; public string State { set { state = value; NotifyObservers(); } get { return state; } } public void RegisterObserver(Observer ob) { observers.Add(ob); } public void RemoveObserver(Observer ob) { observers.Remove(ob); } public void NotifyObservers() { foreach (Observer ob in observers) ob.Update(this); } } public abstract class Observer { public abstract void Update(Subject subject); } public class ConsoleObserver : Observer { public ConsoleObserver(Subject subject) { subject.RegisterObserver(this); } public override void Update(Subject subject) { Console.WriteLine("Subject has changed its state : " + subject.State); } }
调用的方法很简单:
Subject subject = new Subject(); Observer observer = new ConsoleObserver(subject); subject.State = "Kirin Yao";
Subject类维护一个列表,负责观察者的注册和移除。当其状态发生改变时,就调用NotifyObservers方法通知各个观察者。
观察者模式的委托实现
在.NET中,使用委托可以更简单更优雅地实现观察者模式。在上一篇随笔中,最后的示例其实就是一个观察者模式。MainForm为Subject,SubForm为Observer。当MainForm的状态发生改变时(即点击“传值”按钮时),SubForm作为观察者响应来自MainForm的变化。
与上例对应的,用委托实现的观察者模式的代码大致如下:
namespace DelegateSample { class UpdateEventArgs : EventArgs { } class Subject { private string state; public string State { get { return state; } set { state = value; OnUpdate(new UpdateEventArgs()); } } public event EventHandler<UpdateEventArgs> Update; public void ChangeState(string state) { this.State = state; OnUpdate(new UpdateEventArgs()); } private void OnUpdate(UpdateEventArgs e) { EventHandler<UpdateEventArgs> handler = Update; if (handler != null) Update(this, e); } } abstract class Observer { public Subject Subject { get; set; } public Observer(Subject subject) { this.Subject = subject; this.Subject.Update += new EventHandler<UpdateEventArgs>(Subject_Update); } protected abstract void Subject_Update(object sender, UpdateEventArgs e); } class ConsoleObserver : Observer { public ConsoleObserver(Subject subject) : base(subject) { } protected override void Subject_Update(object sender, UpdateEventArgs e) { Subject subject = sender as Subject; if (subject != null) Console.WriteLine("Subject has changed its state : " + subject.State); } } class Program { static void Main(string[] args) { Subject subject = new Subject(); Observer ob = new ConsoleObserver(subject); subject.State = "Kirin Yao"; Console.ReadLine(); } } }
相比传统的观察者模式的实现方式(在Subject中维护一个Observer列表),使用委托避免了Subject与Observer之间的双向引用,Subject作为主题类,对观察者毫无所知,降低了耦合性,语法上也更加优雅。
委托与模板方法模式
模板方法模式封装了一段通用的逻辑,将逻辑中的特定部分交给子类实现。
public abstract class AbstractClass { public void Arithmetic() { SubArithmeticA(); SubArithmeticB(); SubArithmeticC(); } protected abstract void SubArithmeticA(); protected abstract void SubArithmeticB(); protected abstract void SubArithmeticC(); } public class ConcreteClass : AbstractClass { protected override void SubArithmeticA() { //... } protected override void SubArithmeticB() { //... } protected override void SubArithmeticC() { //... } }
然而这种继承方式的模板方法耦合度较高,特别是如果逻辑与其外部实现没有必然的从属关系的时候,用传统的模板方法就显得不那么合适了。
在某种程度上,委托可以看做是一个轻量级的模板方法实现方式,它将逻辑中的特定部分转交给注册到委托的方法来实现。从而替代了继承方式的模板方法模式中,在子类中实现特定逻辑的方式。
public delegate void SubArithmetic(); public class ConcreteClass { public void Arithmetic() { if (SubArithmetic != null) SubArithmetic(); } public SubArithmetic SubArithmetic { get; set; } }
ConcreteClass concrete = new ConcreteClass(); concrete.SubArithmetic = Program.SomeMethod; concrete.Arithmetic();
咋一看在客户端中编写委托的方法似乎还略显麻烦,但值得注意的是,匿名方法和Lambda表达式为我们提供了更加简便的委托语法。在函数式编程日益盛行的今天,我们应该为.NET提供的这种语言特性而感到庆幸。