设计模式学习(二): 观察者模式 (C#)

简介: 《深入浅出设计模式》学习笔记第二章 需求: 开发一套气象监测应用,如图: 气象站,目前有三种装置,温度、湿度和气压感应装置。 WeatherData对象追踪气象站的数据,并更新到布告板,布告板(目前是三个:目前状况、气象统计、天气预报)用来显示目前的天气状况给用户。

《深入浅出设计模式》学习笔记第二章

需求:

开发一套气象监测应用,如图:

气象站,目前有三种装置,温度、湿度和气压感应装置。

WeatherData对象追踪气象站的数据,并更新到布告板,布告板(目前是三个:目前状况、气象统计、天气预报)用来显示目前的天气状况给用户。

初步设计

目前的要求:

1.其中有三个方法分别获得气温、湿度和气压的数据。

2.一旦气象测量被更新,那么这个measurementsChanged()方法就会被调用。

3.一旦有新的数据,者三个布告板(暂时三个)就会马上更新。

4.可扩展,可以开发第三方的布告板。

错误的实现:

错误在哪:

1.变化的地方需要封装。

2.布告板应该统一实现某个带有update方法的接口。

3.不应该针对实现编程,应该针对接口编程。

什么是观察者模式 Observer Pattern

例子:

我们订阅公众号,公众号一旦有新文章就会发送给我们。

当我不再想看文章时,就取消订阅,这时就不会给我发送文章了。

只要公众号还在运营,就一直有人订阅或者取消订阅。

出版者(Publishers) + 订阅者(Subscribers) = 观察者模式(Observer Pattern)

不过我们用的名词不一样,出版者改为主题(Subject),订阅者改为观察者(Observer)

观察者模式定义:

观察者模式定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者(dependents)都会收到通知并自动更新。

观察这模式关系图:

松耦合

两个对象之间的松耦合就是,他们可以交互,但是不清楚对方太多细节。

观察者模式的Subject与Observers之间是松耦合,因为:

1.Subject对于Observer知道的唯一一件事情就是它实现了某个接口。

2.随时可以添加新的Observer。

3.新的Observer出现时,永远不需要更改Subject的代码。

4.我们可以独立的复用Subject或Observer。

5.改变任意的一方并不会影响另外一方。

设计原则

交互对象之间应该尽可能的去实现松耦合设计。

气象应用的具体设计:

C#实现:

接口们:

namespace C02ObserverPattern.Raw.Bases
{
    public interface ISubject
    {
        // 订阅
        void RegisterObserver(IObserver observer);

        // 取消订阅
        void RemoveObserver(IObserver observer);

        // 状态变化时,通知所有观察者
        void NotifyObservers();
    }

    public interface IObserver
    {
        // 气象之变化时,subject会把这些值更新给observers
        void Update(float temp, float humidity, float pressure);
    }

    public interface IDisplayElement
    {
        // 布告板显示
        void Display();
    }
}

WeatherData(subject):

namespace C02ObserverPattern.Raw
{
    public class WeatherData: ISubject
    {
        private readonly HashSet<IObserver> _observers;
        private float _temp, _humidity, _pressure;

        public WeatherData()
        {
            _observers = new HashSet<IObserver>();
        }

        public void RegisterObserver(IObserver observer)
        {
            _observers.Add(observer);
        }

        public void RemoveObserver(IObserver observer)
        {
            _observers.Remove(observer);
        }

        public void NotifyObservers()
        {
            foreach (var observer in _observers)
            {
                observer.Update(_temp, _humidity, _pressure);
            }
        }

        public void MeasurementsChanged()
        {
            NotifyObservers();
        }

        public void SetMeasurements(float temp, float humidity, float pressure)
        {
            _temp = temp;
            _humidity = humidity;
            _pressure = pressure;
            MeasurementsChanged();
        }
    }
}

其中一个布告板:

namespace C02ObserverPattern.Raw
{
    public class CurrentConditionDisplay: IObserver, IDisplayElement
    {
        private float _temp, _humidity;
        
        public CurrentConditionDisplay(ISubject weatherData)
        {
            weatherData.RegisterObserver(this);
        }
        public void Update(float temp, float humidity, float pressure)
        {
            _temp = temp;
            _humidity = humidity;
            Display();
        }

        // 这个布告板只显示温度和湿度
        public void Display()
        {
            Console.WriteLine($"Current conditions:{_temp}℃ and {_humidity}% humidity");
        }
    }
}

测试程序:

namespace C02ObserverPattern
{
    class Program
    {
        static void Main(string[] args)
        {
            WeatherData weatherData = new WeatherData();

            CurrentConditionDisplay currentConditionDisplay = new CurrentConditionDisplay(weatherData);
            StatisticsDisplay statisticsDisplay = new StatisticsDisplay(weatherData);
            // XxxDisplay1 xxxDisplay1 = new XxxDisplay1(weatherData);
            // XxxDisplay2 xxxDisplay2 = new XxxDisplay2(weatherData);

            weatherData.SetMeasurements(10, 20, 30);
            weatherData.SetMeasurements(14, 25, 36);
            weatherData.SetMeasurements(40, 50, 60);

            Console.ReadLine();
        }
    }
}

结果:

 

----------------------------------------------------------------------------------------------------------------------------------------------------------------

使用C#内置的Observer Pattern

IObservable<T> 作为Subject。

IObserver<T> 作为Observer。

先封装一下数据:

namespace C02ObserverPattern.BuiltIn
{
    public struct MyData
    {
        public float Temperature { get; }
        public float Humidity { get; }
        public float Pressure { get; }

        public MyData(float temperature, float humidity, float pressure)
        {
            Temperature = temperature;
            Humidity = humidity;
            Pressure = pressure;
        }
    }
}

WeatherData:

namespace C02ObserverPattern.BuiltIn
{
    public class WeatherDataAlternative : IObservable<MyData>
    {
        private readonly HashSet<IObserver<MyData>> _observers;

        public WeatherDataAlternative()
        {
            _observers = new HashSet<IObserver<MyData>>();
        }
     // 订阅用户,并返回可取消订阅的对象
        public IDisposable Subscribe(IObserver<MyData> observer)
        {
            if (!_observers.Contains(observer))
            {
                _observers.Add(observer);
            }
            return new Unsubscriber(_observers, observer);
        }
     // 发布通知
        public void NotifyMeasurementsChanged(MyData? data)
        {
            foreach (var observer in _observers)
            {
                if (!data.HasValue)
                {
                    observer.OnError(new Exception("No value!!!"));
                }
                else
                {
                    observer.OnNext(data.Value);
                }
            }
        }
     // 关闭这个Subject
        public void WeatherStationClose()
        {
            foreach (var observer in _observers.ToArray())
            {
                observer.OnCompleted();
            }
            _observers.Clear();
            Console.ForegroundColor = ConsoleColor.Red;
            Console.WriteLine("The weather station has been closed.");
        }
     // 取消订阅的类
        private class Unsubscriber : IDisposable
        {
            private readonly HashSet<IObserver<MyData>> _observers;
            private readonly IObserver<MyData> _observer;

            public Unsubscriber(HashSet<IObserver<MyData>> observers, IObserver<MyData> observer)
            {
                _observers = observers;
                _observer = observer;
            }

            public void Dispose()
            {
                if (_observer != null && _observers.Contains(_observer))
                    _observers.Remove(_observer);
            }
        }

    }
}

实现两个布告板

namespace C02ObserverPattern.BuiltIn
{
    public class CurrentConditionDisplayAlternative : IObserver<MyData>
    {
        private IDisposable _unsubscriber;
     // 订阅    
        public virtual void Subscribe(IObservable<MyData> provider)
        {
            if (provider != null)
                _unsubscriber = provider.Subscribe(this);
        }
       // 相当于Display()
        public void OnNext(MyData value)
        {
            Console.WriteLine($"Current condition: {value.Temperature}C degrees and {value.Humidity}% humidity and {value.Pressure} pressure.");
        }
     // 发生错误时调用
        public void OnError(Exception error)
        {
            var original = Console.ForegroundColor;
            Console.ForegroundColor = ConsoleColor.Red;
            Console.WriteLine("Error occurred at Current Condition Display!");
            Console.ForegroundColor = original;
        }
     // 完成时,一般就是取消订阅
        public void OnCompleted()
        {
            Console.WriteLine("Current condition display Task Completed.");
            Unsubscribe();
        }
     // 取消订阅
        public virtual void Unsubscribe()
        {
            Console.WriteLine("Current condition display will unsubscribe from weather station.");
            _unsubscriber.Dispose();
        }
    }
}
namespace C02ObserverPattern.BuiltIn
{
    public class StatisticsDisplayAlternative : IObserver<MyData>
    {
        private IDisposable _unsubscriber;

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

        public void OnNext(MyData value)
        {
            Console.WriteLine($"Statistics: {value.Temperature}, {value.Humidity}, {value.Pressure}.");
        }

        public void OnError(Exception error)
        {
            var original = Console.ForegroundColor;
            Console.ForegroundColor = ConsoleColor.Red;
            Console.WriteLine("Error occurred at Statistics!");
            Console.ForegroundColor = original;
        }

        public void OnCompleted()
        {
            Console.WriteLine("Statistics Task Completed.");
            Unsubscribe();
        }

        public virtual void Unsubscribe()
        {
            Console.WriteLine("Statistics will unsubscribe from weather station.");
            _unsubscriber.Dispose();
        }
    }
}

测试程序:

namespace C02ObserverPattern
{
    class Program
    {
        static void Main(string[] args)
        {
            WeatherDataAlternative weatherDataAlternative = new WeatherDataAlternative();
            CurrentConditionDisplayAlternative currentConditionDisplayAlternative = new CurrentConditionDisplayAlternative();
            currentConditionDisplayAlternative.Subscribe(weatherDataAlternative);
            StatisticsDisplayAlternative statisticsDisplayAlternative = new StatisticsDisplayAlternative();
            statisticsDisplayAlternative.Subscribe(weatherDataAlternative);

            weatherDataAlternative.NotifyMeasurementsChanged(new MyData(25, 75, 120));
            Task.Delay(1000);
            weatherDataAlternative.NotifyMeasurementsChanged(null);
            Task.Delay(1000);
            currentConditionDisplayAlternative.Unsubscribe();
            weatherDataAlternative.NotifyMeasurementsChanged(new MyData(23, 45, 104));
            weatherDataAlternative.WeatherStationClose();

            Console.ReadLine();
        }
    }
}

 

下面是我的关于ASP.NET Core Web API相关技术的公众号--草根专栏:

目录
相关文章
|
2天前
|
设计模式 监控 Java
Kotlin教程笔记(52) - 改良设计模式 - 观察者模式
Kotlin教程笔记(52) - 改良设计模式 - 观察者模式
|
22天前
|
设计模式 传感器
【设计模式】观察者模式(定义 | 特点 | Demo入门讲解)
【设计模式】观察者模式(定义 | 特点 | Demo入门讲解)
32 0
|
9天前
|
设计模式 监控 Java
Kotlin教程笔记(52) - 改良设计模式 - 观察者模式
Kotlin教程笔记(52) - 改良设计模式 - 观察者模式
27 9
|
8天前
|
设计模式 监控 Java
Kotlin教程笔记(52) - 改良设计模式 - 观察者模式
Kotlin教程笔记(52) - 改良设计模式 - 观察者模式
21 2
|
17天前
|
设计模式 监控 UED
设计模式之观察者模式
【10月更文挑战第12天】 观察者模式是一种行为型设计模式,定义了一对多的依赖关系,当一个对象状态改变时,所有依赖它的对象都会自动更新。主要由主题(被观察者)和观察者组成,实现对象间的松耦合,广泛应用于用户界面、事件驱动系统和数据监控等领域。
|
19天前
|
设计模式 监控 Java
Kotlin教程笔记(52) - 改良设计模式 - 观察者模式
本教程详细讲解Kotlin语法,适合深入学习。对于快速掌握Kotlin,推荐“简洁”系列教程。本文特别介绍了观察者模式,包括使用Java API和Kotlin委托属性(如Delegates.observable)实现的方法,旨在帮助开发者更高效地实现和优化观察者模式的应用。
30 3
|
18天前
|
设计模式 安全 Java
C# 一分钟浅谈:设计模式之单例模式
【10月更文挑战第9天】单例模式是软件开发中最常用的设计模式之一,旨在确保一个类只有一个实例,并提供一个全局访问点。本文介绍了单例模式的基本概念、实现方式(包括饿汉式、懒汉式和使用 `Lazy&lt;T&gt;` 的方法)、常见问题(如多线程和序列化问题)及其解决方案,并通过代码示例详细说明了这些内容。希望本文能帮助你在实际开发中更好地应用单例模式,提高代码质量和可维护性。
26 1
|
2月前
|
设计模式 Java 关系型数据库
设计模式——观察者模式
观察者模式介绍、观察者模式优化天气预报案例、JDK 的Observable类和Observer类
设计模式——观察者模式
|
21天前
|
开发框架 缓存 算法
开源且实用的C#/.NET编程技巧练习宝库(学习,工作,实践干货)
开源且实用的C#/.NET编程技巧练习宝库(学习,工作,实践干货)
|
21天前
|
设计模式 监控 Java
Kotlin教程笔记(52) - 改良设计模式 - 观察者模式
Kotlin教程笔记(52) - 改良设计模式 - 观察者模式
25 0