.net设计模式之观察者模式

摘要

    在今天的设计模式系列中我给大家带来了观察者模式,首先我会以一个生动的故事引入观察者模式的应用的场景,然后描述这个场景中出现的问题,最后我们提出观察者模式的解决方案,并给出C#语言实现的代码,最后以.net里的委托-事件方式的观察者模式作为结尾。

故事

小雪是一个非常漂亮的女孩,漂亮的女孩总是有很多的追求者,而且追求者的队伍在不断的变动,随时有人进入这个队伍,也有人退出。男孩们追求女孩时总是表现出120%的关心,当小雪私自游玩时总是不断收到追求者询问小雪位置变动的消息,小雪也不胜其烦,但小雪是如此的一个善良的女孩,她总是打断自己正常的生活回复男孩们的消息。而男孩们由于要不断的关心小雪的位置变化也弄的精疲力竭,而且还影响正常的工作。

在这样一个简单的故事场景中我们发现了什么?来看看小雪和男孩们的烦恼:

男孩们必须不断的询问小雪的位置变化,从而打断正常的工作
    小雪也要不断的接受男孩们的询问,有的时候小雪的位置并没有发生变化,还是要不断的回复男孩们的询问,也影响正常的工作。
    如果给各个男孩们回复问题的方式都不尽相同,小雪还要知道不同的回复方式,而且不断的有新的男孩们增加进来,还不知道未来有什么新的回复方式。

看到这么多烦恼,我们创意无限的Nokia公司给小雪和男孩们提出了解决方案:
    Nokia公司荣誉出品了一款带有GPRS功能的手机,该手机保存着一个订阅位置变化短信通知的电话列表,当该手机检测到位置发生变化就会向这个订阅列表里的所有手机发送短信。看到Nokia这个解决方案,男孩们和小雪都应该松一口气,他们各自都可以按照自己正常的生活习惯,只有状态发生变化时候各自才会进行通信。

观察者模式的解决方案

    在上面Nokia的解决方案中就透露出观察者模式的思想:观察者模式定义了对象之间一对多的依赖,当这个对象的状态发生改变的时候,多个对象会接受到通知,有机会做出反馈。在运行的时刻可以动态的添加和删除观察者。

带着这个定义我们来看看尝试实现上面的观察者模式

首先在观察者模式中我们必须定义一个所有“观察者”都必须实现的接口,这样被观察者向观察者发送消息的时候就可以使用统一的方式,这也符合面相对象原则中的面向接口编程:

using System;
//所有观察者都必须实现
public interface IBoy
{
    //向男孩们显示小雪位置情况,也就是向观察者发送消息,观察者还可以对此做出反馈
    void Show(string address);
}

//男孩A,一个观察者
public class BoyA : IBoy
{
    public void Show(string address)
    {
        //假设经过处理后为韩文的地址
        Console.WriteLine("A:" + address);
    }
}

//男孩B,又一个观察者
public class BoyB : IBoy
{
    public void Show(string address)
    {
        //假设经过处理后为英语的地址
        Console.WriteLine("B:" + address);
    }
}

下面看看小雪的实现,也就是被观察者,主要看看那个订阅的电话列表和怎样将消息通知给观察者.

using System;
using System.Collections.Generic;
public class GPRSMobile
{
    //保存一个观察者列表
    private List<IBoy> boys = null; private string address = "";
    public GPRSMobile()
    {
        boys = new List<IBoy>();
    }
    //添加观察者
    public void AddBoy(IBoy b)
    {
        boys.Add(b);
    }
    public void RemoveBoy(IBoy b)
    {
        boys.Remove(b);
    }
    //通知
    private void Notify(string address)
    {
        for (int i = 0; i < boys.Count; i++)
        {
            boys[i].Show(address);
        }
    }
    //位置发生变化
    public void OnAddressChanaged(string newAddress)
    {
        //假设这里的地址是中文形式的
        Notify(newAddress);
    }
}

看到上面的代码例子,我们可以给观察者模式的实现总结出这样几条规律:

     第一,被观察者必须保存着一个观察者列表。

     第二,所有的观察者必须实现一个统一的接口。

    那观察者模式到底有哪些好处呢?在上面的例子中我们可以看到被观察者仅仅依赖于一个实现了观察者接口的列表,我们可以随时的向这个列表添加观察者,而被观察者无须关注观察者是如何实现的。当我们向观察者族中添加一个新的观察者,被观察者无须作任何改变,新的类型只要实现了观察者接口即可。
在上面的描述中我们仿佛看到了面向接口编程无穷的力量,面向接口编程使实现依赖于接口,也就是实现依赖于抽象。这样在被依赖对象发生改变的时候,只要接口没有发生变化时,依赖对象无须作任何变动。

在现实中存在着很多观察者模式的实例。比如在这个全民炒股的时代,每个持有股票的人总是不断的关注着自己所买的股票的走势,有人天天呆在交易大厅里看着屏幕上股票价格的走势,有人在工作时间盯着电脑里股票软件,为此很多公司采取各种各样的政策来制止这种行为,这样不仅影响正常的上班,且股票交易大厅常常人满为患。如果有这样一个服务,只要你订阅一个短信,这个服务就会在你所关注的股票价格发生变动的时候短信通知你,这样你就可以按照正常的顺序来做你的工作。

.net中的观察者模式

    在.net中,微软给我们带来一个更好的观察者模式的实现:事件-委托.

在Gof的观察者模式中(姑且称之为经典设计模式吧),观察者必须实现一个统一的接口,在.net里这个接口由委托的签名来保证了,.net里的委托就是一个安全的函数指针(之所以说安全是与以前的C指针相比的,C的函数指针并不包括函数的签名比如参数等东西,所以可以传递一个并不是你期望的函数进去,导致运行时出错,由于这种错误在运行时发生,很难检查出来)。Ok,现在以一个.net的委托-事件的例子结束今天的观察者模式吧。

描述:这是一个控制台程序,程序接收一个0到100之间整型的输入,程序接收到输入后开始一个从0到100的循环,当循环到你输入的数字的时候做一些处理,我们将以两种方式来描述这个实例,先用常规的方式,然后采用委托-事件的方式

public class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("Please Input a 0-100 Number:");
        int input = Console.Read();
        if (input < 0 || input > 100)
        {
            Console.WriteLine("Error");
        }
        for (int i = 0; i <= 100; i++)
        {
            if (i == input)
            {
                //屏幕输出
                Console.WriteLine(i.ToString());
                //弹出提示框
                MessageBox.Show(i.ToString());
                //可能还有其他处理
            }
        }
    }
}

看到这个例子有什么感觉?循环的代码和处理的代码混在一起,可能还有未知的处理方式添加进来。耦合度非常高。再看看.net的处理方式吧

namespace Observer
{
    //定义一个委托,这里定义了观察者方法的签名,就是一个协议吧
    public delegate void NumberEventHandler(object sender, NumberEventArgs e);
    //要传递哪些参数到观察者?在这里定义,注意,要继承自EventArgs
    public class NumberEventArgs : EventArgs
    {
        public NumberEventArgs(int number)
        {
            _number = number;
        }
        private int _number;
        public int Number
        {
            get
            { return _number; }
            set
            { _number = value; }
        }
    }
    //观察者模式中的主题
    public class Subject
    {
        //定义一个事件,就是委托的实例了
        public event NumberEventHandler NumberReached;
        public void DoWithLoop(int number)
        {
            for (int i = 0; i <= 100; i++)
            {
                //触发事件的条件到了
                if (i == number)
                {
                    NumberEventArgs e = new NumberEventArgs(i); OnNumberReached(e);
                }
            }
        }
        //注意,这个方法定义为保护的,虚拟的,代表子类还可以进行覆盖,改变触发事件的行为
        //甚至可以不触发事件
        protected virtual void OnNumberReached(NumberEventArgs e)
        {
            //判断事件是否为null,也就是是否绑定了方法
            if (NumberReached != null) NumberReached(this, e);
        }
    }
    public class MainProgram
    {
        public static void Main()
        {
            Console.WriteLine("Please Input a 0-100 Number:");
            int input = Console.Read();
            if (input < 0 || input > 100)
            {
                Console.WriteLine("Error");
            }
            Subject s = new Subject();
            //给事件绑定方法,静态的
            s.NumberReached += new NumberEventHandler(msgbox_NumberReached);
            MainProgram mp = new MainProgram();
            //给事件绑定方法,实例方法
            s.NumberReached += new NumberEventHandler(mp.console_NumberReached);
            s.DoWithLoop(input);
            Console.Read();
        }
        void console_NumberReached(object sender, NumberEventArgs e)
        {
            Console.WriteLine(e.Number.ToString());
        }
        static void msgbox_NumberReached(object sender, NumberEventArgs e)
        {
            MessageBox.Show(e.Number.ToString());
        }
    }
}

虽然这个例子代码多多了,但是是值得的,事件触发的地方和处理的地方完全分离了,循环的位置不再需要知道有多少个方法正等着处理它

总结

    经过几篇设计模式文章的介绍,也许有人会觉得设计模式一直在尝试解决几个问题:解藕,封装变化。设计模式一直在为可维护性,可扩展性,灵活性努力着。所以学习设计模式并不是了解设计模式的原型,重要的是了解设计模式的场景和目的,这样你也可以在你自己的工作中总结出自己的设计模式。
有人说中国的数学教育是个错误,学习数学并不是学习那些定理公式,学习那些东西永远是跟在别人的后面,学习数学应该注重数学史的学习,循着数学发展的历史,了解前人是怎样分析问题,解决问题,学习前人的“渔”,并不仅仅是为了得到“鱼”。

本来上面的文章已经写定了,但今天看一MVP的文章又有点新的感触,觉得上面的总结又有点偏颇,学习模式重要的是她的精髓,但是“作为初学者即使知道所有设计原则但是却不知道如何在项目应用”。是的,也许学习设计模式也要从“量变”引起“质变”。大量的应用,先不管是否是过度设计,到一定的时候也许就会得到思想上的升华。

时间: 2024-10-08 12:41:20

.net设计模式之观察者模式的相关文章

【设计模式】观察者模式

生活中,当某件事发生时,应该通知所有的相关者.例如,上课地点有变,要通知所有学习这门课的同学. 在软件设计中,当一个对象的状态发生变化是,如何通知与它相关的所有对象,就是我们今天要谈到的观察者模式. 观察者模式 概述 定义了一种一对多的依赖关系.让多个观察者对象同事监听某一个主题对象.这个主题对象在状态发生变化时,会通知所有的观察者对象,使它们能够自动更新自己. 实际上,观察者模式所做的工作就是在解除耦合.让耦合的双方都依赖于抽象,而不是依赖于具体.从而使得各自的变化都不会影响另一边的变化. 结

设计模式:观察者模式

设计模式:观察者模式 : http://www.cnblogs.com/li-peng/archive/2013/02/04/2892116.html 观察者模式是我们经常用的一个模式,比如在用wcf做服务的时候通知客户端做一些操作一般用设计者模式. 今天做一个订报纸的小例子来理解一下观察者模式  出版者+订阅者=观察者模式 用下边的图来说就是    人民日报+订阅者=观察者模式 只要是订阅了人民日报的人,有了新报纸就会送到订阅者那里去, 当你不想订的时候取消就不订阅就不会再收到报纸了. //报

设计模式之-观察者模式

该文代码来自设计模式之惮一书. 观察者模式定义: 观察者模式也叫做发布订阅模式. 观察者模式中的角色: 1.被观察者 被观察者职责为管理观察者并通知观察者. 2.观察者 对接受到的消息进行处理. /// <summary> /// 被观察者 /// </summary> public abstract class Subject { /// <summary> /// 观察者集合 /// </summary> private List<IObserva

设计模式(16) 观察者模式(OBSERVER)C++实现

意图: 定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新. 动机: 将一个系统设计成一系列相互协作的类有一个常见的副作用:需要维护相关对象之间的一致性. 观察者模式定义一种交互,即发布-订阅: 一个对象当自身状态发生改变时,会发出通知,但是并不知道谁是他的接收者,但每个接收者都会接收到通知,这些接受者称为观察者. 作为对通知的响应,每个观察者都将查询目标状态,然后改变自身的状态以和目标状态进行同步. 使用场景: 使对象封装为独立的改变和使用:

&lt;&lt;Head First设计模式&gt;&gt;之观察者模式学习

1.概念 定义了对象之间的一对多依赖关系,当一个对象(主题对象)的状态改变时,它的所有依赖者(观察者对象)都会收到通知并自动更新. 2.优势 观察者模式实现了主题对象与观察者对象之间的松耦合,当有新的观察者时,无需修改主题对象的代码,只需要新的观察者对象实现接口.在程序运行的过程中,可以随时注册和删除观察者而不影响主体对象. 观察者模式使主题(Subject)对象和观察者(Observer)对象都可以被轻易地复用. 3.Java中观察者模式 Java API有内置的观察者模式,java.util

大话设计模式_观察者模式(Java代码)

观察者模式:定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象.这个主题对象在状态发生变化时,会通知所有观察者对象,使他们自己能够自动更新自己.简单描述:1个Subject类(可以是抽象类可以是接口),可以有多个具体Subject类(继承抽象Subject),此具体Subject持有多个观察者的引用,在此具体Subject状态发生变化的时候调用这些观察者的相应方法.另外,这些持有的引用是抽象的,不是具体的,而这些引用所指向的对象时具体的观察者(即需要作出更新的具体观察者,这些具体

设计模式-观察者模式(Observer Pattern)

今天看了看观察者模式,有点小小的体会,从以下3点说明之 : 1.看一下经典的观察者模式图 2.讲个故事,说一下逻辑 有一个男人(Subject),他通过一定的手段(attach)拥有了3个女人(Observer):老婆(concreteObserver1),情人(concreteObserver2),小3(concreteObserver3).这3个女人都不知情,但是都想知道男人的月收入情况.而月收入需要通过工资卡(concreteSubject)来体现.所以,每当发工资(setState)时,

常用设计模式之观察者模式 + 事件委托

常用设计模式之观察者模式 + 事件委托 作用及UML (摘自<大话设计模式>) Code 1 abstract class Subject{ 2 protected String state; 3 public void setState(String state){this.state = state;} 4 public String getState(){return this.state;} 5 6 private List<Observer> observers = ne

跟我学设计模式视频教程——观察者模式,迭代器模式

课程视频 观察者模式 迭代器模式 课程笔记 课程笔记 课程代码 课程代码 新课程火热报名中 课程介绍 跟我学设计模式视频教程--观察者模式,迭代器模式,布布扣,bubuko.com

设计模式之观察者模式C++实现

观察者模式定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新. 观察者模式结构图如下: 举例: 气象系统有三个部分分别是气象站(获取实际气象数据的物理装置),WeatherData对象(用来追踪来自气象站的数据,并更新布告板)和布告板(显示目前天气状况给用户看).WeatherData对象知道如何根物理气象站联系,以取得更新信息.WeatherData对象会随机更新三个布告板的显示:目前状况(温度,湿度,气压).气象统计和天气预报.我们的工作是建立一