美文网首页
观察者(Observer)

观察者(Observer)

作者: 非空白 | 来源:发表于2017-06-17 22:58 被阅读0次

意图

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

结构

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

动机

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

适用性

  • 当一个抽象模型有两个方面, 其中一个方面依赖于另一方面。将这二者封装在独立的对象中以使它们可以各自独立地改变和复用;
  • 当对一个对象的改变需要同时改变其它对象, 而不知道具体有多少对象有待改变;
  • 当一个对象必须通知其它对象,而它又不能假定其它对象是谁。换言之, 你不希望这些对象是紧密耦合的。

优缺点

  • 目标和观察者间的抽象耦合(接口)。目标只知道有一系列的观察者,但不知道它们所属的具体类;
  • 支持广播通信。目标(Subject)发送的通知被自动广播给所有已向该目标登记的所有对象(Observer);
  • 意外的更新。如果一个观察者(Observer)误操作目标(Subject)的状态,可能会导致其他观察者连锁反应式的错误更新。

注意事项

  • 当观察者(Observer)依赖多个目标(Subject)时,考虑扩展Update接口,把目标对象(Subject)作为识别参数。
// 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)对象自动触发,或者由客户端手动触发。
//  示例:状态变更,目标(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();  // 手动通知(容易遗忘)
    }
}
  • 确保目标(Subject)在触发通知之前,处于一致状态。特别是在子类集成Subject时容易发生,可用模板(Template)模式实现;
// 存在状态不一致的错误代码
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

相关文章

  • Android 开发--观察者模式

    观察者 模式 有两个 角色,观察者observer ,被观察者observable。 Observer 是个接口,...

  • RxJava

    Observable & Observer 观察者(Observer)对可观察对象(Observable)发射的数...

  • RxJava源码解析

    基本框架 Observable (可观察者,即被观察者) Observer (观察者) subscribe (订阅...

  • RxJava Observable 使用和源码阅读

    ReactiveX 系列文章目录 Observable/Observer 的使用 过去的 Observer 观察者...

  • Swift - RxSwift的使用详解5(观察者1: AnyO

    一、观察者(Observer)介绍 观察者(Observer)的作用就是监听事件,然后对这个事件做出响应。或者说任...

  • Rxjava 2.0

    添加依赖 Observable(被观察者)/Observer(观察者) Flowable(被观察者)/Subscr...

  • 10. RxJava

    RxJava Observable: 被观察者Observer: 观察者subscribe: 订阅observe...

  • 30天学RxSwift——Observer

    Observer即观察者,在RxSwift中有许多名称的Observer,ObserveBase,Anonymou...

  • KVO实现

    观察对象添加观察者( observer ): observer中需要实现一下方法:

  • RxJava2.0初学知识回顾

    1、RxJava的重要组成 Observable (可观察者,即被观察者)、Observer (观察者)、subs...

网友评论

      本文标题:观察者(Observer)

      本文链接:https://www.haomeiwen.com/subject/zzkjqxtx.html