设计模式的故事---观察者模式

意图:

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

适用性:

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

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

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


 模拟一个《奔跑吧兄弟》中 撕名牌 的游戏

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方法的参数就是目标对象本身,这基本上是目标对象能传递的最大数据集合了,基本上可以适应各种情况的需求。

时间: 2024-10-11 15:27:30

设计模式的故事---观察者模式的相关文章

设计模式-单例模式,观察者模式

序言 咳咳,今天起,我要把对设计模式的理解心得,用全新的案例去分析,分享给大家.希望大家能够喜欢. 观察者模式 举例阐述:游戏情节,一颗小男孩,丢到众多鬼子附近,爆炸啦,根据炸弹的威力计算爆炸后鬼子的血量,假定有些鬼子有防具,有些鬼子没有防具. 分析:这种情况,使用观察者模式是比较理想的,因为观察者模式的就是是处理对象间一对多的依赖关系的,当一个对象发生变化,其它依赖他的对象都要得到通知并更新. 定义:在观察者模式中,上述小男孩被称为主题,而小鬼子们就被称为观察者. 下面我用代码,把举例给演示出

【读书笔记】读《JavaScript设计模式》之观察者模式

一.定义 在事件驱动的环境中,比如浏览器这种持续寻求用户关注的环境中,观察者模式(又名发布者-订阅者(publisher-subscripber)模式)是一种管理人与其任务之间的关系(确切地讲,是对象及其行为和状态之间的关系)的得力工具.用JavaScript的话来说,这种模式的实质就是你可以对程序中某个对象的状态进行观察,并且在其发生改变时能够得到通知. 二.例子 我们需要一个发布者的构造函数,它为该实例定义了一个类型为数组的属性,用来保存订阅者的引用. function Publisher(

设计模式学习之观察者模式(Observer,行为型模式)(7)

1.观察者模式又叫做发布-订阅模式. 2.观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象.这个主题对象在状态发生变化时,会通知所有观察者对象,使它们能够自动更新自己. 3.使用观察者模式的好处:维护相关对象间的一致性.我们不希望为了维持一致性而使各类紧密耦合,这样会给维护.扩展和重用都带来不便.什么时候使用观察者模式:当一个对象的改变需要同时改变其他对象的时候,而且它不知道具体有多少对象有待改变时,应该考虑使用观察者模式. private static void M

【C++深入浅出】设计模式学习之观察者模式

前言 前两天学习了weak_ptr以后还是不甚明了,一则需要实际应用去锤炼,二来就是不懂观察者模式. 正文 观察者模式又叫发布-订阅模式,定义了一种一对多的依赖关系,让多个观察者对象同时监听某一主题对象,这个主题对象在状态发生改变时,会通知所有的观察者对象,使他们能够自动更新自己. 通过一张visio的UML图片介绍一下子功能关系. subject类:抽象通知者类,一般用一个抽象类或者接口实现,把所有对观察者的引用都放到一个集合里,每个抽象通知者类可以有任意数量的观察者,抽象通知者类提供一个接口

我理解设计模式C++实现观察者模式Observer Pattern

概述: 近期中国股市起起伏伏,当然了起伏就用商机,小明发现商机后果断想入市,买入了中国证券,他想在电脑client上,网页上,手机上,iPad上都能够查看到该证券的实时行情,这样的情况下我们应该怎么设计我们的软件呢?我们能够这样:小明的全部client上都订阅中国证券这个股票,仅仅要股票一有变化,全部的client都会被通知到而且被自己主动更新. 这就是我们的观察者模式,她定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时, 全部依赖于它的对象都得到通知并被自己主动更新. 类图与实例:

设计模式:(4)观察者模式

现实生活中,这样的例子太多了,一个对象的状态受另外一个对象的影响.比如,进度条根据上传的百分比而变化,红灯停绿灯行.....这样的业务数不胜数.甚至我们有时候心情也是随着很多经历而变化.在开发过程中,这样的业务当然也是很多的,但是,稍有不慎,我们可能会实现出比较麻烦的代码.而设计模式中有一种模式对于解决这样的业务具有很好作为,那就是观察者模式.简而言之就是:我们有一个目标,还有很多观察者,当目标变化,观察者们将作出相关的变化. 观察者模式: 看看书上的定义,也是行为模式 意图: 定义对象间的一种

【设计模式系列】--观察者模式

在前面的博文中,小编介绍设计模式中的撩妹模式,不知道小伙伴们有没有从中get到技巧呢?今天这篇博文,咱们继续来学习设计模式的相关知识,今天小编向大家介绍的模式是观察者模式,还请小伙伴多多指教,小编会从什么是原型观察者模式.观察者模式的结构图.观察者模式的demo以及观察者模式的特点和应用场景等方面一一进行介绍,希望对有需要的小伙伴有帮助.      什么是观察者模式? 观察者模式(有时又被称为发布(publish )-订阅(Subscribe)模式.模型-视图(View)模式.源-收听者(Lis

大话设计模式C++版——观察者模式

观察者模式是一种类似于消息分发的模式,用于一个任务需要被多个对象监听的场景,或者成员对象需要反向通知类对象的情况,是一种很有用的设计模式. 这里以大话设计模式中的例子为例,办公室员工A.B.C在看股票看电影,这时老板回来了,被A.B.C重金贿赂后的前台MM发出通知给A.B.C,A.B.C收到通知后赶紧关电脑,关股票窗口,装作在干活. 1.观察者接口 class IObserver { public: virtual ~IObserver() {} virtual void OnEvent(TSt

[转] 浅析JavaScript设计模式——发布-订阅/观察者模式

前一段时间一直在写CSS3的文章 一直都没写设计模式 今天来写写大名鼎鼎观察者模式 先画张图 观察者模式的理解 我觉得还是发布-订阅模式的叫法更容易我们理解 (不过也有的书上认为它们是两种模式……) 这就类似我们在微信平台订阅了公众号 当它有新的文章发表后,就会推送给我们所有订阅的人 我们可以看到例子中这种模式的优点 我们作为订阅者不必每次都去查看这个公众号有没有新文章发布, 公众号作为发布者会在合适时间通知我们 我们与公众号之间不再强耦合在一起.公众号不关心谁订阅了它, 不管你是男是女还是宠物