设计模式:观察者模式(二)——C#内置接口实现观察者模式

通过C#的内置观察者接口实现观察者模式

1. 接口介绍

C#内部提供了IObservable和IObserver两个泛型接口,IObservable是可观察的,就是主题(Subject)要实现的接口,IObserver就是观察者需要实现的接口,接口定义如下:

//T:提供通知信息的对象。
public interface IObservable<out T>
{
    //通知提供程序观察程序将接收通知。
    IDisposable Subscribe(IObserver<T> observer);
}
//T:提供通知信息的对象。
public interface IObserver<in T>
{
    //通知观察者提供程序已完成发送基于推送的通知。
    void OnCompleted();
    //通知观察者提供程序遇到错误情况。
    void OnError(Exception error);
    //向观察者提供新数据。
    void OnNext(T value);
}

2. Demo背景

这里与上一篇一致:设计一个气象观测站,测量温度、湿度、气压等,会有多种公告板如气温布告板,舒适度布告板,天气预报布告板等等。每当天气数据变化时,这些布告板的数据就需要相应自动更新。

3. 代码设计

  • 首先建一个WeatherData,这是主题向观察者传递的数据。
public struct WeatherData
{
    private float temperature;
    private float humidity;
    private float pressure;

    public float Temperature { get => temperature; }
    public float Humidity { get => humidity; }
    public float Pressure { get => pressure; }

    public void SetMessureMents(float temperature, float humidity, float pressure)
    {
        this.temperature = temperature;
        this.humidity = humidity;
        this.pressure = pressure;
    }
}
  • 新建WeatherDataProvider类作为主题的提供方,提供订阅与取消订阅的方法。
public class WeatherDataProvider : IObservable<WeatherData>
{
    //观察者列表
    private List<IObserver<WeatherData>> observers;
    public WeatherDataProvider()
    {
        observers = new List<IObserver<WeatherData>>();
    }
    //定义一个可销毁的内部类,所以继承自IDisposable接口
    //观察者注册时生成对象,注销时,调用Dispose(),取消订阅并释放资源
    private class Unsubscriber : IDisposable
    {
        private List<IObserver<WeatherData>> _observers;
        private IObserver<WeatherData> _observer;

        public Unsubscriber(List<IObserver<WeatherData>> observers, IObserver<WeatherData> observer)
        {
            this._observers = observers;
            this._observer = observer;
        }
        public void Dispose()
        {
            if (_observer != null && _observers.Contains(_observer))
            {
                _observers.Remove(_observer);
            }
        }
    }

    //通知提供程序观察程序将接收通知。
    public IDisposable Subscribe(IObserver<WeatherData> observer)
    {
        if (!observers.Contains(observer))
        {
            observers.Add(observer);
        }
        //返回可销毁的内部类,当观察者取消订阅时,调用该类的Dispose(),释放资源
        return new Unsubscriber(observers, observer);
    }
    //通知所有观察者更新数据数据
    public void SendWeatherData(Nullable<WeatherData> weather)
    {
        foreach (var observer in observers)
        {
            observer.OnCompleted();
            observer.OnNext(weather.Value);
        }
    }
}
  • 观察者Display类实现
public class Display : IObserver<WeatherData>
{
    private IDisposable unsubscriber;
    private string displayName;

    public Display(string name)
    {
        this.displayName = name;
    }

    public virtual void Subscribe(IObservable<WeatherData> provider)
    {
        if (provider != null)
        {
            unsubscriber = provider.Subscribe(this);
        }
    }

    public virtual void Unsubscribe()
    {
        unsubscriber.Dispose();
    }
    //通知观察者提供程序已完成发送基于推送的通知。
    public void OnCompleted()
    {
        Console.WriteLine($"{displayName}天气数据发送完毕");
    }
    //通知观察者提供程序遇到错误情况。
    public void OnError(Exception error)
    {
        Console.WriteLine($"在提供天气数据时,发生错误。");
    }
    //向观察者提供新数据。
    public void OnNext(WeatherData value)
    {
        Console.WriteLine($"{displayName}当前天气 => 温度:{value.Temperature}, 湿度:{value.Humidity}, 气压{value.Pressure}。 ");
    }
}
  • 测试代码与运行效果
class Program
{
    static void Main(string[] args)
    {
        WeatherData weather = new WeatherData();
        WeatherDataProvider provider = new WeatherDataProvider();
        Display display1 = new Display("布告板1");
        Display display2 = new Display("布告板2");
        Console.WriteLine("---------加入布告板1---------");
        display1.Subscribe(provider);
        weather.SetMessureMents(20,20,20);
        provider.SendWeatherData(weather);
        Console.WriteLine("---------加入布告板2---------");
        display2.Subscribe(provider);
        weather.SetMessureMents(30, 30, 30);
        provider.SendWeatherData(weather);
        Console.WriteLine("----------取消布告板1----------");
        display1.Unsubscribe();
        weather.SetMessureMents(23, 23, 23);
        provider.SendWeatherData(weather);
        Console.WriteLine("运行结束");
        Console.ReadLine();
    }
}

4. 源码地址

https://github.com/DonyGu/DesignPatterns

原文地址:https://www.cnblogs.com/donyblog/p/11373057.html

时间: 2024-10-08 13:10:10

设计模式:观察者模式(二)——C#内置接口实现观察者模式的相关文章

python之路基础-(二)内置函数、函数、装饰器

内置函数 python内置了以下函数,可以根据情况来使用 一.数学相关函数 divmod():取商和余数 >>> divmod(99,10) (9, 9) abs():取绝对值 >>> abs(-10) 10 len():查看序列长度 >>>list = [11,22,33,44] >>>r = len(list) >>>print(r)4 二.功能相关函数chr():在ascii码表中根据数字找出对应的字母 >

angular指令(二)--内置指令

一.基础ng 属性指令: ? ng-href? ng-src? ng-disabled? ng-checked? ng-readonly? ng-selected? ng-class? ng-style 布尔属性的有: ng-disabled.ng-readonly.ng-checked.ng-selected 类布尔属性有: ng-href.ng-src 二.在指令中使用子作用域 ? ng-app ? ng-controller ? ng-include 使用ng-include可以加载.编译

VB.net笔记 (二)内置对象

ASP.NET内置对象  转 (1)简述ASP.NET内置对象. 答:ASP.NET提供了内置对象有Page.Request.Response.Application.Session.Server.Mail和Cookies.这些对象使用户更容易收集通过浏览器请求发送的信息.响应浏览器以及存储用户信息,以实现其他特定的状态管理和页面信息的传递. (2)简述Response对象. 答:Response对象用来访问所创建的并客户端的响应,输出信息到客户端,它提供了标识服务器和性能的HTTP变量,发送给

[Chromium学习之二]浏览器内置URL

在Chrome浏览器地址栏输入chrome://chrome-urls/可以看到内置的一些URL.这些URL提供一些工具帮助使用者.如下: List of Chrome URLs chrome://accessibility chrome://appcache-internals chrome://apps chrome://blob-internals chrome://bookmarks chrome://cache chrome://chrome chrome://chrome-urls

二、内置基础数据类型

两种数据类型: 1.语言内置的数据类型 1)数值型: ① 整数型: 无符号:unit8,unit16,unit32,unit64 有符号:int8, int16, int32, int64 下列X表示X位的操作系统 unit = unitX, int = intX, unitptr = X为的指针 类型的别名: byte = unit8 rune = int32 ② 浮点型: float32(单精度浮点) float64(双精度浮点) ③ 虚数型(很新奇): complex64 complex1

thinkphp5 内置接口开发与使用

最近的一个项目在用tp5,对于tp3都几乎没用过的我来说~~~ tp5最好的一点就是对接口的单独封装,只要严格按照要求一步一步来就可以成功了 开启命令行: 配置环境变量 安装tp5项目 cmd进入项目目录,运行php think,出现如下内容,则表示命令行开启成功 具体指令参考手册 https://www.kancloud.cn/manual/thinkphp5/122951 创建自定义命令行(接口) 配置command.php文件,目录在application/command.php <?ph

php深入学习笔记二( 函数内置函数 )

1. call_?user_?func_?array 调用用户自定义函数,第一个参数是函数名, 第二个参数是函数的参数 必须是是一索引数组 function foobar($arg, $arg2) { echo __FUNCTION__, " got $arg and $arg2\n"; } class foo { function bar($arg, $arg2) { echo __METHOD__, " got $arg and $arg2\n"; } } //

tornado内置接口调用顺序initialize\prepare...

一. initialize方法 首先, 该方法是框架预留的一个初始化时加载自定义内容的钩子, 其会在http请求方法之前调用 二. prepare方法 预处理方法, 在执行对应的请求方法之前调用. http请求方法如下图 000 三. set_default_headers方法和write_error方法 具体作用及用法见前面笔记 四. on_finish方法 在请求处理结束后调用, 在该方法中可进行资源回收或日志处理等一些操作. 注意不要在该方法中进行数据的返回 五. 以上各方法调用顺序如下:

匿名函数、内置函数与模块

一.匿名函数 Python使用lambda来创建匿名函数,不再使用def语句这样标准的形式定义一个函数 lambda只是一个表达式,函数体比def简单很多 lambda的主体是一个表达式,而不是一个代码块.仅仅能在lambda表达式中封装有限的逻辑进去 lambda函数拥有自己的名称空间,且不能访问自有参数列表之外或全局名称空间里的参数 虽然lambda函数看起来只能写一行,却不等同于C或C++的内联函数,后者的目的是调用小函数时不占用栈内存从而增加运行效率 适合临时的一次性的使用场景 语法: