美文网首页
观察者(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
    

    相关文章

      网友评论

          本文标题:观察者(Observer)

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