• 观察者模式


     什么是观察者模式?

    观察者模式(有时又被称为发布(Publish)-订阅(Subscribe)模式、模型-视图(View)模式、源-收听者(Listener)模式或从属者模式)是软件设计模式的一种。在此种模式中,一个目标物件管理所有相依于它的观察者物件,并且在它本身的状态改变时主动发出通知。这通常透过呼叫各观察者所提供的方法来实现。此种模式通常被用来实现事件处理系统。

    观察者设计模式定义了对象间的一种一对多的依赖关系,以便一个对象的状态发生变化时,所有依赖于它的对象都得到通知并自动刷新。---百度百科

    顾名思义,就是观察某个对象和某对象被观察的模式,观察者Observer也是订阅者Subscriber,也是监听者,而主题Subject也是Publisher也是源。这种模式提供一对多的关系,多个观察者对应1个Subject.

    我们先来看一下简单的例子,以下例子转载自这里,且稍作修改。一个人A观察水是否煮沸,若水沸腾则关闭电源。其中,人A是Observer(观察者),水是Subject(主题),当水沸腾时,将通知人水沸腾了。当然,现实中水不能直接跟人说“我沸腾啦……”,代码里面自然是要写成一个方法的。

    首先,有一个观察者的类Person:

     public class Person
        {
            public void Update(string str)
            {
                System.Console.WriteLine(str + "关电源");
            }
        }

    有一个被观察的类Water,被观察者基本含有3个部分:

    1. 添加观察者,一个Subject至少对应一个Observer,在类中需要维护它自己的观察者;
    2. 移除观察者,若某个观察者没有必要再观察它时,可将其移除;
    3. 当观察者关心的状态变化时,主动通知观察者;
        public class Water
        {
            private Person _person;
            private bool _isBoiled;
    
            public Water()
            {
                _isBoiled = false;
            }
    
            public void SetBoiled()
            {
                _isBoiled = true;
                NotifyObserver();
            }
    
            public void AddObserver(Person person)
            {
                this._person = person;
            }
    
            public void RemoveObserver()
            {
                if (_person != null)
                    _person = null;
            }
    
            public void NotifyObserver()
            {
                if (_isBoiled && _person != null)
                {
                    _person.Update("水开了,");
                    _isBoiled = false;
                }
            }
        }

    在这个例子中只有一个观察者person,观察对象water的状态。

        class Program
        {
            static void Main(string[] args)
            {
                Person person = new Person();
                Water water = new Water();
                water.AddObserver(person);
                water.SetBoiled();
                System.Console.ReadLine();
            }
        }

    上述例子是直接使用SetBoiled()来强制将状态变成true,达到通知的目的,在与硬件交互时,可能是自动探测温度,当温度达到100℃时,将_isboiled设为true,这里就直接强制赋值了。

    我们知道,观察模式是对象间一对多的关系,以上述例子为例,一壶水正在烧,可能A在观察它的温度,B在观察它是否沸腾,但是在它的温度变化时总是会通知所有的观察者A、B……当水的状态,而A/B也会相应作出一些动作来改变自身,那么AB都将有共同的一个方法Update(),因为可能会有更多的观察者CDEF……所有,我们写一个接口IObserver:

        public interface IObserver
        {
            void Update(PublisherBase publisher);
        }

    那么所有的观察者都必须实现这个更新的操作,本例中包括温度观察者和沸腾观察者:

        public class TemperatureObserver : IObserver
        {
            public void Update(PublisherBase publisher)
            {
                Water water =(Water) publisher  ;
                System.Console.WriteLine("温度:" + water.GetTemperature() + "     状态:" + water.GetStatus().ToString());
            }
        }
        public class BoiledObserver:IObserver
        {
            string doSomething;
            public BoiledObserver(string doSomething)
            {
                this.doSomething = doSomething;
            }
    
            public void Update(PublisherBase publisher)
            {
                Water water = (Water)publisher;
                if (water.GetTemperature() >= 100)
                {
                    System.Console.WriteLine("状态:" + water.GetStatus().ToString());
                    System.Console.WriteLine("BoiledObserver:" + doSomething);
                }
                else
                {
                    System.Console.WriteLine("没开");
                }
            }
        }

    因为Subject有了更多的观察者,那么它现在就需要维护一个List来维护众多的观察者对象了,当然,无论对象都多少个,Subject必然有维护这些对象的增删通知操作:

        public class SubjectBase
        {
            protected bool _isChanged;
            protected List<IObserver> _observers = new List<IObserver>();
    
            public SubjectBase()
            {
                _isChanged = false;
            }
    
            public void AddObserver(IObserver observer)
            {
                _observers.Add(observer);
            }
    
            public void RemoveObserver(IObserver observer)
            {
                _observers.Remove(observer);
            }
    
            public void NotifyObserver()
            {
                if (_isChanged)
                {
                    foreach (IObserver observer in _observers)
                    {
                        observer.Update( this);
                    }
                }
    
            }
        }

    被观察者水继承SubjectBase,由于不能直接获取水温的变化,我们人为的提供水温变化的方法类模仿这样一个动作:

        public class Water:SubjectBase
        {
            private double _temperature;
            private WaterStatus _status;
    
            public Water()
            {
                this._temperature = 0;
                this._status = WaterStatus.Cold;
            }
    
            public Water(IObserver observer)
            {
                this.AddObserver(observer);
            }
    
            public double GetTemperature()
            {
                return _temperature;
            }
    
            public WaterStatus GetStatus()
            {
                return _status;
            }
    
            public void Change(double temperature)
            {
                _temperature = temperature;
                if (_temperature < 40)
                    _status = WaterStatus.Cold;
                else if (_temperature >= 40 && _temperature < 60)
                    _status = WaterStatus.Warm;
                else if (_temperature >= 60 && _temperature < 100)
                    _status = WaterStatus.Hot;
                else 
                    _status = WaterStatus.Boiled;
                this._isChanged = true;
                this.NotifyObserver();
            }
    
        }
    
    
        public enum WaterStatus
        {
            Cold,
            Warm,
            Hot,
            Boiled
        }

    看一下如何调用,假设有1个温度观察者,2个沸腾观察者,首先要把他们维护进观察者列表,当水温变化时,water将会Notify各个观察者,而观察者就会自动Update各自的行为或状态:

        class Program
        {
            static void Main(string[] args)
            {
                TemperatureObserver tempObserver = new TemperatureObserver();
                BoiledObserver boiledObserver1 = new BoiledObserver("关闭电源……");
                BoiledObserver boiledObserver2 = new BoiledObserver("继续保温……");
                Water water = new Water(tempObserver);
                water.AddObserver(boiledObserver1);
                water.AddObserver(boiledObserver2);
                water.Change(45);
                System.Console.WriteLine();
                water.Change(80);
                System.Console.WriteLine();
                water.Change(100);
                System.Console.WriteLine();
                System.Console.ReadLine();
            }
        }

    运行的结果:

    可以看得出来,每次Change温度之后,water会通知所有的观察者。当然,在实际应用中,更常见的一个例子是,当后台数据更新时,将自动更新前台界面上的数据,这时候,前台界面是观察者,后台数据是被观察者。

  • 相关阅读:
    01 Windows编程——Hello World
    图像处理基础知识
    集成IDE anaconda
    Python中的正则表达式
    Introduction of Machine Learning
    Linux命令——diff、patch
    sed & awk 概述
    Linux行编辑器——ed
    Linux命令——w、who、whoami、lastlog、last
    【问题】统计系统上有多少个用户
  • 原文地址:https://www.cnblogs.com/YunGy/p/5063984.html
Copyright © 2020-2023  润新知