观察者(Observer)

2017-06-17  本文已影响0人  非空白

意图

定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖它的对象都得到通知并自动更新。

结构

观察者结构图 观察者时序图

动机

将一个系统分割成一系列相互协作的类有一个常见的副作用:需要维护相关对象间的一致性。我们不希望为了维持一致性而使各类紧密耦合,因为这样降低了它们的可重用性。

适用性

优缺点

注意事项

// Subject class
public void Notify()
{
    foreah(Observer o in Observers)
    {
          o.Update(this);
    }
}
// ConcreteObserver class
public void Update(Subject subject)
{
      if(subject is ConcreteSubject1)
      {
          // 来自目标1的通知
      }
      else if(subject is ConcreteSubject2)
      {
          // 来自目标2的通知
      }
      ...
}
//  示例:状态变更,目标(Subject)自动触发。
public class ConcreteSubject : Subject
{
      private object state;
      public object SetState(object state)
      {
          this.state = state;
          this.Notify();   // 自动触发
      }

      public void Notify()
      {
            foreach(Observer o in Observers)
            {
                  o.Update();
            }
      }
}
// 示例
public class App
{
    public static void Main(string[] args)
    {
          ConcreteSubject subject = new ConcreteSubject();
          subject.Attach(Observer); // 登记观察者
          ...
          // 状态每一次变更,都会自动通知观察者
          subject.SetState(newState1); 
          subject.SetState(newState2); 
          ...
    }
}
// 示例:客户端手动触发通知
public class ConcreteSubject
{
      private object state;
      public object SetState(object state)
      {
          this.state = state;
      }

      public void Notify()
      {
            foreach(Observer o in Observers)
            {
                  o.Update();
            }
      }
}
// 示例
public class App
{
    public static void Main(string[] args)
    {
          ConcreteSubject subject = new ConcreteSubject();
          subject.Attach(Observer); // 登记观察者
          ...
          // 设置完一系列状态后,一次性通知观察者(避免观察者繁琐更新)。
          subject.SetState(newState1); 
          subject.SetState(newState2); 
          ...
          subject.Notify();  // 手动通知(容易遗忘)
    }
}
// 存在状态不一致的错误代码
public class ConcreteSubject : Subject
{
    ...
    public override void SetState(object state)
    {
        base.Notify();   // 提前触发,导致状态不一致。
        this.state = state;
    }
}

更改为模板实现方式:

public class Subject
{
    ...
    // 模板方法
    public void SetState(object state)
    {        
        this.InternalSetState(state);
        this.Notify();
    }
    protected virtual void InternalSetState(object state)
    {
        this.state = state;
    }
}
public class ConcreteSubject : Subject
{
    ...
    // 子类只需要实现个性化的状态处理
    protected override void InternalSetState(object state)
    {
        ...
        this.state = state;
    }
}
public void Attach(Observer observer, InterestType interest);


示例

模拟两个不同类型的图形控件,分别显示当前的时间。

实现(C#)

示例结构图
using System;
using System.Threading;
using System.Collections.Generic;

// 目标主题基类
public class Subject
{
    private readonly List<Observer> observers = new List<Observer>();

    public void Attach(Observer o)
    {
        this.observers.Add(o);
    }

    public void Detach(Observer o)
    {
        this.observers.Remove(o);
    }

    public void Notify()
    {
        foreach(Observer o in this.observers)
        {
            o.Update(this);
        }
    }
}

// 观察者
public abstract class Observer
{
    public abstract void Update(Subject theChangedSubject);
}

// 具体的目标主题,以3秒间隔发出通知
public class ClockTimer : Subject
{
    private Timer timer;

    public ClockTimer()
    {
        this.timer = new Timer(this.Tick, null, 0, 3000);
    }

    public void Tick(object state)
    {
        this.Now = DateTime.Now;
        this.Notify();
    }

    public DateTime Now { get; private set;}
}

// 模拟时钟控件1
public class DigitalClock : Observer
{
    private readonly ClockTimer subject;

    public DigitalClock(ClockTimer subject)
    {
        this.subject = subject;
        this.subject.Attach(this);  // 注册监听
    }

    public override void Update(Subject theChangedSubject)
    {
        // 确认是否为目标监听对象
        if(this.subject == theChangedSubject)
        {
            Console.WriteLine("1.DigitalClock : " + this.subject.Now);
        }
    }
}

// 模拟时钟控件2
public class AnalogClock : Observer
{
    private readonly ClockTimer subject;

    public AnalogClock(ClockTimer subject)
    {
        this.subject = subject;
        this.subject.Attach(this); // 注册监听
    }

    public override void Update(Subject theChangedSubject)
    {
        // 确认是否为目标监听对象
        if(this.subject == theChangedSubject)
        {
            Console.WriteLine("2. AnalogClock : " + this.subject.Now);
        }
    }
}

public class App
{
    public static void Main(string[] args)
    {
        ClockTimer timer = new ClockTimer();
        DigitalClock digitalClock = new DigitalClock(timer);
        AnalogClock analogClock = new AnalogClock(timer);

        Console.WriteLine("please enter any key to exit..\n");
        Console.Read();
    }
}

// 控制台输出:
//  please enter any key to exit..
//  1.DigitalClock : 2017/6/17 22:37:24
//  2. AnalogClock : 2017/6/17 22:37:24
//  1.DigitalClock : 2017/6/17 22:37:27
//  2. AnalogClock : 2017/6/17 22:37:27
//  1.DigitalClock : 2017/6/17 22:37:30
//  2. AnalogClock : 2017/6/17 22:37:30
//  1.DigitalClock : 2017/6/17 22:37:33
//  2. AnalogClock : 2017/6/17 22:37:33
上一篇 下一篇

猜你喜欢

热点阅读