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

需求:

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

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

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(, , );
            weatherData.SetMeasurements(, , );
            weatherData.SetMeasurements(, , );

            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);
                }
            }
        }

        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);
        }

        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(, , ));
            Task.Delay();
            weatherDataAlternative.NotifyMeasurementsChanged(null);
            Task.Delay();
            currentConditionDisplayAlternative.Unsubscribe();
            weatherDataAlternative.NotifyMeasurementsChanged(, , ));
            weatherDataAlternative.WeatherStationClose();

            Console.ReadLine();
        }
    }
}

设计模式学习之“观察者模式” [C#]的更多相关文章

  1. Java设计模式学习记录-观察者模式

    前言 观察者模式也是对象行为模式的一种,又叫做发表-订阅(Publish/Subscribe)模式.模型-视图(Model/View)模式. 咱们目前用的最多的就是各种MQ(Message Queue ...

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

    前言 前两天学习了weak_ptr以后还是不甚明了,一则需要实际应用去锤炼,二来就是不懂观察者模式. 正文 观察者模式又叫发布-订阅模式,定义了一种一对多的依赖关系,让多个观察者对象同时监听某一主题对 ...

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

    1.观察者模式又叫做发布-订阅模式. 2.观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象.这个主题对象在状态发生变化时,会通知所有观察者对象,使它们能够自动更新自己. 3 ...

  4. php设计模式学习之观察者模式

    什么都不说,先看代码: interface userOperateImpl { public function operate($username); } class userLoginLog imp ...

  5. Java-马士兵设计模式学习笔记-观察者模式-模拟Awt Button

    一.概述 Java 的Awt是 Observer模式,现用Java自己模拟awt中Button的运行机制 二.代码 1.Test.java import java.text.DateFormat; i ...

  6. Java-马士兵设计模式学习笔记-观察者模式-AWT简单例子

    1.AWT简单例子 TestFrame.java import java.awt.Button; import java.awt.Frame; import java.awt.event.Action ...

  7. Java-马士兵设计模式学习笔记-观察者模式-读取properties文件改成单例模式

    一.概述 1.目标:读取properties文件改成单例模式 二.代码 1.Test.java class WakenUpEvent{ private long time; private Strin ...

  8. Java-马士兵设计模式学习笔记-观察者模式-OOD 封装Listener

    一.概述 childe类中的是关联监听者dad的,若要再增加监听者,会很不方便,且要修改代码.好的方法是封装监听者类,用addListener()方法动态添加监听者 二.代码 1.Test.java ...

  9. Java-马士兵设计模式学习笔记-观察者模式-OOD 封装event

    把小孩醒来时的具体情况封装成事件类 Test.java class WakenUpEvent{ private long time; private String location; private ...

随机推荐

  1. SQL分组查询

      把数据分成多个逻辑组,以便能对每个组的数据进行操作,对每个组进行聚集计算. 1.创建分组 分组是在SELECT语句的GROUP BY子句中建立的 例子:         SELECT vendId ...

  2. 《阿里巴巴Java开发手册v1.2》解析(编程规约篇)

    之前在乐视天天研究各种底层高大上的东西,因为我就一个人,想怎么弄怎么弄.如今来了新美大,好好研读一下<阿里巴巴Java开发手册v1.2>.还要对这么看似简单的东西解析一番.毕竟现在带团队, ...

  3. jQuery方法输出有几个checkbox框被选中

    每选中一个多选框,输出有多少个选中 <!DOCTYPE html> <html lang="en"> <head> <meta chars ...

  4. TC358743XBG:HDMI转MIPI CSI参考设计

    TC358743XBG参考设计电路图如下, 功能HDMI转MIPI CSI ,通信方式:IIC,分辨率1920*1080,封装形式BGA64.

  5. 自上而下,逐步揭开PHP解析大整数的面纱

    遇到的问题 最近遇到一个PHP大整数的问题,问题代码是这样的 $shopId = 17978812896666957068; var_dump($shopId); 上面的代码输出,会把$shopId转 ...

  6. Sublime Text 安装插件

    Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等.还可自定义键绑定,菜单和工具栏.Sublime Text 的主要功能包括:拼写检查,书签,完整的 ...

  7. 201521123083 《Java程序设计》第10周学习总结

    1. 本周学习总结 2. 书面作业 本次PTA作业题集异常,多线程 1.finally题目4-2 1.1 截图你的提交结果(出现学号) 1.2 4-2中finally中捕获异常需要注意什么? 一个tr ...

  8. 测试与发布(Alpha版本)

    [Alpha阶段]测试报告 1.测试找出的BUG (1).这种情况刷新就好 (2).文件必须10个才行,多余10个的部分不会进入查重的部分,少于会出错: (3).文件保存在d:\files,由于有些原 ...

  9. 【Alpha】——Seventh Scrum Meeting

    一.今日站立式会议照片 二.每个人的工作 成员 昨天已完成的工作 今天计划完成的工作 李永豪 将项目做成APK 用户界面改善 郑靖涛 协助设计账目一览表板块 用户界面改善 杨海亮 查询功能测试 用户界 ...

  10. 201521123062《Java程序设计》第10周学习总结

    1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结异常与多线程相关内容. 2. 书面作业 1.finally 题目4-2 1.1 截图你的提交结果(出现学号) 1.2 4-2中fin ...