翻译:在我们使用的NET FRAMEWORK类库中发现设计模式(2)

观察者模式

 

Observer :观察者,需要目标对象在发现变化时能获得通知并调用本身某个更新的方法。

Subejct : 目标,即当本身发现变化时能通知每一个观察者。

 

    好的 OO 设计不但强调封装而且还强调松耦合,换句话说,类要尽可能的封装自己内部的私有细节并且应该将类与类之间的强依赖减小到最少。在大多数应用里,类并不是孤立的,它们之间往往会相互交互和影响。这里有一个很普遍的类与类交互场景:当一个对象( subject )发生改变时,另外一个对象( Observer )要求能收到这个 (subject 改变的 ) 通知。例如:几个窗体控件需要在一个按钮点击后能够改变它们的呈现。一个简单的解决方法就是在 Subject 对象状态改变时直接去调用 Observer 的某一个特定的方法。这种方法将会引入一大把问题:

Subject 需要知道将要调用 Observer 哪一个方法,这种情况将会导致 Subject 与某个特定的 Observer 紧耦合。此外,如果你需要添加另外几个 Observer ,你不得不在 Subject 里添加调用每一个 Observer 方法的代码。如果 Observer 的数量是动态变化的,那么这将更加复杂,你将倒在这种无法维护的混乱之中。

       一个有效的解决方法是使用观察者模式。使用这种方法,你能够将 Subject 与它的那些 Observer 解耦以便能在设计时和运行时轻松的添加和删除任意数量的 Observer Subject 维护着一个 Observer 的列表,每当 Subject 的状态有所改变时,它能够依次通知列表中所有的 Observer 。请看如下的示例代码:

public abstract class CanonicalSubjectBase

{

private ArrayList _observers = new ArrayList();

public void Add(ICanonicalObserver o)

    {

        _observers.Add(o);

}

public void Remove(ICanonicalObserver o)

    {

        _observers.Remove(o);

    }

public void Notify()

    {

        foreach(ICanonicalObserver o in _observers)

        {

            o.Notify();

        }

    }

}

public interface ICanonicalObserver

{

    void Notify();

}

    所有的扮演 Observer 角色的类都实现了 ICanonicalObserver 接口,所有的 Subject 都必须继承自 CanonicalSubjectBase ,如果有一个新的 Observer 想监测 Object ,那么只需要添加此 Observer 的方法而不需要更改 Subject 中的任何一行代码! 而且你会发现 Object 再也不用直接依赖于那些 Observer (因为开始的那个方式是 Object 必须调用每个 Observer 的某个方法,译者注)了,而是仅仅依赖于 ICanonicalObserver 接口。

        尽管四人帮的观察者模式能够解决一些问题,但是这个方式还是有些缺陷,因为 Subejct 必须继承自 CanonicalSubjectBase ,所有的 Observer 必须实现 ICanonicalObserver 接口。(也就是说从一种意义上的紧耦合转移成了另一种意义上的耦合,译者注) , 让我们回头看看那个窗体按钮控件的例子吧,解决的方案就呈现在我们眼前: NET Framework 使用委托和事件来解决这个问题!如果你曾经在 ASP.NET WinForm 中写过程序,那么你可能已经使用过委托和事件,事件就是 Subejct ,而委托就是 Observer ,让我们来看看利用事件来实现的观察者模式的例子:

public delegate void Event1Hander();

public delegate void Event2Handler(int a);

public class Subject

{

public Subject(){}

public Event1Hander Event1;

  public Event2Handler Event2;

public void RaiseEvent1()

  {

     Event1Handler ev = Event1;

     if (ev != null) ev();

   }

public void RaiseEvent2()

  {

     Event2Handler ev = Event2;

     if (ev != null) ev(6);

  }

}

public class Observer1

{   public Observer1(Subject s)

    {

        s.Event1 += new Event1Hander(HandleEvent1);

        s.Event2 += new Event2Handler(HandleEvent2);

}

public void HandleEvent1()

    {

      Console.WriteLine("Observer 1 - Event 1");

}

public void HandleEvent2(int a)

    {

        Console.WriteLine("Observer 1 - Event 2");

    }

}

     窗体按钮控件暴露了一个在按钮点击时能够通知 Observer 的点击事件,任何一个需要响应该事件的 Observer 仅仅只需要向这个事件注册一个委托( new Event1Hander method )便是一个委托,注册一个委托就是 += Event1 事件,译者注)。窗体按钮控件并不依赖于任何一个潜在的 Observer ,每一个 Observer 仅仅只需要知道这个事件的委托签名格式就 OK 了(在这个实例里委托就是 EventHandler ),因为 EventHandler 是一个委托类型而不是一个接口,所以每个 Observer 不需要去实现一个格外的接口,如果一个 Observer 已经有了一个符合委托签名格式的方法,那么它仅仅只需要把这个方法注册到 Subject 的事件中去。通过使用委托和事件,观察者模式能将 Subject Observer 解耦(也就是 Subject 不需要显式的去调用 Observer 的某一个方法)!

posted @ 2006-03-18 11:29  Zhongkeruanjian  阅读(1778)  评论(4编辑  收藏  举报