什么是观察者模式?
观察者模式(有时又被称为发布(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个部分:
- 添加观察者,一个Subject至少对应一个Observer,在类中需要维护它自己的观察者;
- 移除观察者,若某个观察者没有必要再观察它时,可将其移除;
- 当观察者关心的状态变化时,主动通知观察者;
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会通知所有的观察者。当然,在实际应用中,更常见的一个例子是,当后台数据更新时,将自动更新前台界面上的数据,这时候,前台界面是观察者,后台数据是被观察者。