你所在位置:首页Asp.net开发 → 翻译:在我们使用的NET FRAMEWORK类库中发现设计模式(2)

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

发布时间:2019-06-12

 

观察者模式

 

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 的某一个方法)!

 



摘自 把爱好作为自己工作的人


上一篇:翻译:在我们使用的NET FRAMEWORK类库中发现设计模式(3)
下一篇:翻译:在我们使用的NET FRAMEWORK类库中发现设计模式(1)