zoukankan      html  css  js  c++  java
  • 设计模式的故事---观察者模式

    意图:

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

    适用性:

    当一个抽象模型有两个方面, 其中一个方面依赖于另一方面。将这二者封装在独立的对象中以使它们可以各自独立地改变和复用。

    当对一个对象的改变需要同时改变其它对象, 而不知道具体有多少对象有待改变。

    当一个对象必须通知其它对象,而它又不能假定其它对象是谁。换言之, 你不希望这些对象是紧密耦合的。


     模拟一个《奔跑吧兄弟》中 撕名牌 的游戏
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Security.Cryptography;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace DesignerModel.Observer
    {
        public abstract class IObserver
        {
            public abstract void Update(string msg);
        }
        /// <summary>
        /// 大广播
        /// </summary>
        public class Subject
        {
            private List<Observer> observers = new List<Observer>();
    
            public void Attach(Observer o)
            {
                observers.Add(o);
            }
            public void Detach(Observer o)
            {
                observers.Remove(o);
            }
    
            public void NotifyObservers(string msg)
            {
                foreach (var item in observers)
                {
                    item.Update(msg);
                }
            }
    
        }
        /// <summary>
        /// 游戏者
        /// </summary>
        public class Observer : IObserver
        {
            public Observer(string name)
            {
                Name = name;
            }
            public string Name { get; set; }
    
            public override void Update(string msg)
            {
                Console.WriteLine(this.Name + "收到信息:" + msg);
            }
            public string CreateNum()
            {
                RNGCryptoServiceProvider csp = new RNGCryptoServiceProvider();
                byte[] byteCsp = new byte[10];
                csp.GetBytes(byteCsp);
                return BitConverter.ToString(byteCsp);
            }
        }
    
    
        public class GameRule
        {
            private Subject Subject { get; set; }
    
            public GameRule(Subject subject)
            {
                Subject = subject;
            }
            /// <summary>
            /// 撕名牌
            /// </summary>
            /// <param name="ob1"></param>
            /// <param name="ob2"></param>
            /// <returns></returns>
            public Observer RipTheNameplate(Observer ob1, Observer ob2)
            {
                Random rand1=new Random(1);
                Random rand2=new Random(5);
                int num1 = rand1.Next(1,1000);
                int num2 = rand2.Next(1, 1000);
                if (num1>num2)
                {
                    Subject.NotifyObservers(ob1.Name+"Out");
                    return ob1;
                }
                else
                {
                    Subject.NotifyObservers(ob2.Name + "Out");
                    return ob2;
                }
            }
        }
    
    
        public static class Client
        {
            public static void Run()
            {
                Subject sub = new Subject();
    
                Observer Observer1 = new Observer("邓超");
                Observer Observer2 = new Observer("郑凯");
                Observer Observer3 = new Observer("Baby");
                Observer Observer4 = new Observer("包贝尔");
                Observer Observer5 = new Observer("陈赫");
                Observer Observer6 = new Observer("李晨");
    
    
                sub.Attach(Observer1);
                sub.Attach(Observer2);
                sub.Attach(Observer3);
                sub.Attach(Observer4);
                sub.Attach(Observer5);
                sub.Attach(Observer6);
                //让邓超和郑凯撕名牌
                new GameRule(sub).RipTheNameplate(Observer1, Observer2);
               
                Console.Read();
            }
        }
    }
     

    目标和观察者之间的关系

         按照模式的定义,目标和观察者之间是典型的1对多的关系,但是注意,如果观察者只有一个,也可以是1对1的 关系,这也使得在处理一个对象的状态变化会影响到另一个对象的时候,也可以考虑使用观察者模式。

    同样的,一个观察者也可以观察多个目标,如果观察者为多个目标定义的通知更新方法都是Update方法的话,这会带来麻烦,因为需要接受多个目标的通知,如果是一个Update方法,那就需要在方法内部区分,到底这个更新的通知来自于哪一个目标,不同的目标有不同的后续操作。

      一般情况下,观察者应该是不同的观察者目标定义不同的回调方法,这样实现最简单,不需要在update方法内部进行区分。

       单向依赖,在观察者模式中,观察者和目标是单向依赖的,只有观察者依赖于目标,而目标是不依赖于观察者的。

    他们之间的主动权掌握在目标手里,只有目标知道什么时候需要通知观察者,在整个过程中,观察者始终是被动的。被动的等待目标的通知。等待目标传值给它。

    对目标而言,所有的观察者都是一样的,目标会一视同仁的对待。当然也可以通过在目标中进行控制,实现有区别的对待观察者。比如某些状态变化了,只需要通知部分观察者。但那是属于稍微变形的用法了。不属于标准的,原始的观察者模式。

    基本的实现说明:

    1,具体的目标实现对象要能维护观察者的注册信息,最简单的实现方案就如同前面的例子那样,采用一个集合来保存观察者的注册信息。

    2,具体的目标实现对象需要维护引起通知的状态,一般情况下目标自身的变形使用的情况下,也可以是别的对象的状态。

    3,具体的观察者实现对象需要能接受目标的通知,能够接受目标传递的数据,或者是能够主动去获取目标的数据,并进行后续处理

    4,如果是一个观察者目标观察多个目标,那么在观察者的更新方法里,需要去判断是来自哪一个目标的通知。一种简单的解决方案就是扩展Update方法,比如在方法里面多传递一个参数进行区分等;还有一个更简单的方法,那就是干脆定义不同的回调方法。

    命名建议

    1,观察者模式又被称为 发布--订阅模式

    2,目标接口的定义,建议在名称后面跟Subject

    3,观察者接口的定义,建议在名称后面跟observer

    观察者接口的更新方法,,建议名称为Update,当然方法的参数可以根据情况而定,参数个数,类型不限。

    触发通知的时机:

      在实现观察者模式的时候,一定要注意触发通知的时机,一般情况下,是在完成了状态维护后触发,因为通知会传递数据,不能够先通知后改数据,这很容易出问题,会导致观察者和目标对象的状态不一致。

    比如,目标一触发通知,就有观察者来取值,结果目标还没有更新数据,这就明显地造成了错误。

    观察者模式的两种模式:

    推模式:目标对象主动向观察者推送目标的详细信息,不管观察者是否需要,推送的信息通常是目标对象的全部或部分数据,相当于是在广播通信

    拉模式:目标对象在通知观察者的时候,只传少量信息。如果观察者需要更具体的信息。有观察者主动到目标对象中取,相当于是观察者从目标对象中拉数据。一般这种模式的实现中,会把目标对象自身通过update方法传递给观察者,这样在观察者需要获取数据的时候,就可以通过这个引用来获取了。

    推模式是假定目标对象知道观察者需要的数据,而拉模式是目标对象不知道观察者具体需要什么数据,没有办法的情况下,干脆把自身传给观察者,让观察者自己去按需求取值。

    推模式可能会使得观察者对象难以复用,因为观察者定义的update方法是按需而定义的,可能无法兼顾没有考虑到的情况。这就意味着出现新情况的时候,就可能需要提供新的update方法,或者干脆重新实现观察者。而拉模式就不会造成这样的情况,因为拉模式下,update方法的参数就是目标对象本身,这基本上是目标对象能传递的最大数据集合了,基本上可以适应各种情况的需求。

  • 相关阅读:
    零基础入门:实时音视频技术基础知识全面盘点
    IM开发干货分享:如何优雅的实现大量离线消息的可靠投递
    flask_apscheduler 定时任务框架
    小程序使用 editor 富文本编辑器填坑指南
    万年深坑:mpVue 中的坑
    js利用canvas绘制爱心
    【字节跳动21届提前批】面试手撕代码——电梯调度算法
    Maximum call stack size exceeded 报错
    未安装less-loder
    05 vue前端工程化
  • 原文地址:https://www.cnblogs.com/anbylau2130/p/4609961.html
Copyright © 2011-2022 走看看