观察者模式

 什么是观察者模式?

观察者模式(有时又被称为发布(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会通知所有的观察者。当然,在实际应用中,更常见的一个例子是,当后台数据更新时,将自动更新前台界面上的数据,这时候,前台界面是观察者,后台数据是被观察者。

 

posted @ 2015-12-21 17:04  修大人  阅读(366)  评论(0编辑  收藏  举报