UP主在这里简要的介绍下观察者模式:(详细的介绍请大家自行阅读相关书籍)
在这里先给出观察者模式的一个简要的定义:
发布者+订阅者=观察者模式
是不是感觉这个事件模型很像,别着急,UP主在最后会进行观察者模式与事件模型的讨论,供大家批评。
观察者的准确定义是:
观察者模式 定义了对象之间的一对多依赖,这样一来,当一个对象发生改变时,他的所有依赖者都会收到通知并会自动更新。
另外,观察者模式可不只是 简单的 主题发布,观察者收到通知并执行 的过程,
观察者也可以从主题处 拉 来信息,不接收主题的某些信息,甚至可以选择不再注册主题。
这些特性请大家看文中给出的两个程序。
具体大家请看书上的简图:
观察者模式提供了一种对象设计,让主题与观察者之间松耦合,让对象间的依赖降到了最低,建立有弹性的OO系统,能够应对变化。
书中给出了一个松耦合的观察者的例,UP主把它写出来了:
//为了松耦合,一切对象皆在接口的层面上进行操作
//主测试程序
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ObserverPattern
{
internal class Program
{
static void Main(string[] args)
{
WeatherData weatherData = new WeatherData();
FirstConditionDisplay firstConditionDisplay = new FirstConditionDisplay(weatherData);
SecondConditionDisplay secondConditionDisplay = new SecondConditionDisplay(weatherData);
ThirdConditionDisplay thirdConditionDisplay = new ThirdConditionDisplay(weatherData);
weatherData.SetMessurements(37, 25, 1.2f);
Console.WriteLine("*****************************");
weatherData.SetMessurements(5, 19, 1.0f);
Console.WriteLine("*****************************");
weatherData.RemoveObserver(secondConditionDisplay);
weatherData.SetMessurements(41, 19, 1.0f);
Console.WriteLine("*****************************");
weatherData.RegisterObserver(secondConditionDisplay);
weatherData.SetMessurements(0, 30, 1.0f);
Console.WriteLine("*****************************");
weatherData.NotifyObserver();
Console.WriteLine("*****************************");
Console.ReadKey();
}
}
}
//天气信息接口
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ObserverPattern
{
public interface ISubject
{
void RegisterObserver(IObserver ob);
void RemoveObserver(IObserver ob);
void NotifyObserver();
}
}
//天气数据类,也是主题类
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ObserverPattern
{
public class WeatherData:ISubject
{
private ArrayList observers;
private float temperature;
private float humidity;
private float pressure;
public WeatherData()
{
observers = new ArrayList();
}
public void NotifyObserver()
{
for(int i =0; i <observers.Count; i++)
{
IObserver observer = (IObserver)observers[i];
observer.Update(temperature, humidity, pressure);
}
}
public void RegisterObserver(IObserver ob)
{
observers.Add(ob);
}
public void RemoveObserver(IObserver ob)
{
int i = observers.IndexOf(ob);
if (i >= 0)
{
observers.RemoveAt(i);
}
}
public void MessurementsChanged()
{
NotifyObserver();
}
public void SetMessurements(float temperature, float humidity, float pressure)
{
this.temperature = temperature;
this.humidity = humidity;
this.pressure = pressure;
MessurementsChanged();
}
}
}
//布告板接口, 也是观察者类的接口,
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ObserverPattern
{
public interface IObserver
{
void Update(float temperature, float humidity, float pressure);
}
}
//展示布告板信息的接口,就一个函数,display
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ObserverPattern
{
public interface IDisplayElement
{
void Display();
}
}
//第一个观察者
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ObserverPattern
{
public class FirstConditionDisplay : IObserver, IDisplayElement
{
private float temperature;
private float humidity;
private float pressure;
private ISubject weatherData;
public FirstConditionDisplay(ISubject weatherData)
{
this.weatherData = weatherData;
weatherData.RegisterObserver(this);
}
public void Display()
{
Console.WriteLine("1 号布告板:现在天气状况是:温度" + temperature + "°C , 湿度: " + humidity + "%, 压力:"+ pressure+ " * 100000 Pa");
}
public void Update(float temperature, float humidity, float pressure)
{
this.temperature = temperature;
this.humidity = humidity;
this.pressure = pressure;
Display();
}
}
}
//第二个观察者
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ObserverPattern
{
public class SecondConditionDisplay : IObserver, IDisplayElement
{
private float temperature;
private float humidity;
private float pressure;
private ISubject weatherData;
public SecondConditionDisplay(ISubject weatherData)
{
this.weatherData = weatherData;
weatherData.RegisterObserver(this);
}
public void Display()
{
Console.WriteLine("2 号布告板:现在天气状况是:温度" + temperature + "°C , 湿度: " + humidity + "%, 压力:" + pressure + " * 100000 Pa");
}
public void Update(float temperature, float humidity, float pressure)
{
this.temperature = temperature;
this.humidity = humidity;
this.pressure = pressure;
Display();
}
}
}
//第三个观察者
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ObserverPattern
{
public class ThirdConditionDisplay : IObserver, IDisplayElement
{
private float temperature;
private float humidity;
private float pressure;
private ISubject weatherData;
public ThirdConditionDisplay(ISubject weatherData)
{
this.weatherData = weatherData;
weatherData.RegisterObserver(this);
}
public void Display()
{
Console.WriteLine("3 号布告板:现在天气状况是:温度" + temperature + "°C , 湿度: " + humidity + "%, 压力:" + pressure + " * 100000 Pa");
}
public void Update(float temperature, float humidity, float pressure)
{
this.temperature = temperature;
this.humidity = humidity;
this.pressure = pressure;
Display();
}
}
}
//后面可以新建观察者,并可以在主测试函数里向发布者(主题)订阅。
另外,.NET C# 野提供了内置的观察者模式,有两个接口:
IObservable
IObserver
下面UP主也会一并给出C# 中内置的的观察者模式的应用:
//主测试函数
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ObserverPatternUseObservable
{
internal class Program
{
static void Main(string[] args)
{
WeatherDataPublisher publisher = new WeatherDataPublisher();
CurrentConditionDisplay currentDisplay = new CurrentConditionDisplay();
StatisticsConditionDisplay statisticsDisplay = new StatisticsConditionDisplay();
//订阅当前天气展示板
IDisposable currentDisplayUnsubscriber = publisher.Subscribe(currentDisplay);
//订阅气温统计展示板
IDisposable statisticsDisplayUnsubscriber =publisher.Subscribe(statisticsDisplay);
for (int i = 0;i<3 ; i++)
{
WeatherData weatherData = new WeatherData();
Console.WriteLine("请输入温度,湿度,压力");
string input = Console.ReadLine();
var array = input.Split(',');
weatherData.temperature = array[0];
weatherData.humility = array[1];
weatherData.pressure = array[2];
Console.WriteLine("");
//将输入的新的天气数据传给天气数据发布器
publisher.ReciveNewData(weatherData);
Console.WriteLine("=============================");
}
}
}
}
//天气数据类
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ObserverPatternUseObservable
{
public class WeatherData
{
/// <summary>
/// 气温
/// </summary>
public string temperature { get; set; }
/// <summary>
/// 湿度
/// </summary>
public string humility { get; set; }
/// <summary>
/// 气压
/// </summary>
public string pressure { get; set; }
}
}
//天气数据发布类
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ObserverPatternUseObservable
{
internal class WeatherDataPublisher : IObservable<WeatherData>
{
List<IObserver<WeatherData>> observers = new List<IObserver<WeatherData>>();
/// <summary>
/// 订阅主题,将观察者添加到列表中
/// </summary>
/// <param name="observer"></param>
/// <returns></returns>
public IDisposable Subscribe(IObserver<WeatherData> observer)
{
observers.Add(observer);
return new Unsubscribe(this.observers, observer);
}
/// <summary>
/// 通知已订阅的观察者
/// </summary>
/// <param name="weatherData"></param>
private void Notify(WeatherData weatherData)
{
foreach (var observer in observers)
{
observer.OnNext(weatherData);
}
}
/// <summary>
/// 接收最新的天气数据
/// </summary>
/// <param name="weatherData"></param>
public void ReciveNewData(WeatherData weatherData)
{
Notify(weatherData);
}
}
}
//观察者向主题取消订阅类:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ObserverPatternUseObservable
{
/// <summary>
/// 取消订阅类
/// </summary>
public class Unsubscribe:IDisposable
{
List<IObserver<WeatherData>> observers;
IObserver<WeatherData> observer;
public Unsubscribe(List<IObserver<WeatherData>> observers
, IObserver<WeatherData> observer)
{
this.observer = observer;
this.observers = observers;
}
public void Dispose()
{
if (this.observers != null)
{
this.observers.Remove(observer);
}
}
}
}
//观察者类:布告板类抽象类:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ObserverPatternUseObservable
{
public abstract class WeatherDisplayBase : IObserver<WeatherData>
{
public virtual void OnCompleted()
{
}
public virtual void OnError(Exception error)
{
}
public abstract void OnNext(WeatherData value);
}
}
//第一个观察者类:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ObserverPatternUseObservable
{
public class StatisticsConditionDisplay : WeatherDisplayBase
{
List<float> temperatures = new List<float>();
public override void OnNext(WeatherData value)
{
float temperature;
if (float.TryParse(value.temperature, out temperature))
{
temperatures.Add(temperature);
}
Console.WriteLine("------------------");
Console.WriteLine("温度统计板");
Console.WriteLine(string.Format("平均温度:{0}\n最高温度:{1}\n最低温度:{2}",
temperatures.Average(), temperatures.Max(), temperatures.Min()));
Console.WriteLine("------------------");
}
}
}
//第二个观察者类:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ObserverPatternUseObservable
{
public class CurrentConditionDisplay : WeatherDisplayBase
{
public override void OnNext(WeatherData value)
{
Console.WriteLine("------------------");
Console.WriteLine("当前天气状况板");
Console.WriteLine(string.Format("温度:{0}\n湿度:{1}\n气压:{2}",
value.temperature, value.humility, value.pressure));
Console.WriteLine("------------------");
}
}
}
那好,如果你都把上面的项目仔细的调试运行,你就会细细的发现文章开头所讲的概念了。
那么,问题来了,像不像事件模型?
是的,很像,我将一个简单的邮局发报纸的事件模型项目给写出来,如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace 事件测试Demo
{
public class Post
{
public delegate void EventHandler(object sender);
public event EventHandler Evt;
public void Run()
{
Console.WriteLine("邮局:新报纸出来喽");
Evt(this);
}
}
public class PersonA
{
public void PersonAReaction(object sender) //A的事件处理程序
{
Console.WriteLine("PersonA:新报纸来啦,赶紧用来垫东西!");
}
}
public class PersonB
{
public void PersonBReaction(object sender) //B的事件处理程序
{
Console.WriteLine("PersonB:新报纸来啦,赶紧阅读阅读看看有啥新闻!");
}
}
public class PersonC
{
public void PersonCReaction(object sender)//C的事件处理程序
{
Console.WriteLine("PersonC:新报纸来啦,下一年的报纸费用还得付,怎么办,快没钱了!");
}
}
class Program
{
static void Main(string[] args)
{
Post p = new Post(); //邮局
PersonA a = new PersonA(); //订阅者A
PersonB b = new PersonB(); //订阅者B
PersonC c = new PersonC(); //订阅者C
p.Evt += new Post.EventHandler(a.PersonAReaction); //订阅报纸
p.Evt += new Post.EventHandler(b.PersonBReaction); //订阅报纸
p.Evt += new Post.EventHandler(c.PersonCReaction); //订阅报纸
//触发事件,发报纸喽
p.Run();
Console.ReadKey();
}
}
}
对观察者与事件之间做一下小小的讨论:
1.事件就是观察者模式的一种应用
2.观察者模式----> 事件
主题----> 事件源
主题状态----> 事件(事件源发送给监听者的信息)
观察者----> 监听者
3.实际运用中
目前实际运用中,我们的工具基本都提供好了接口调用,一般使用的话,我们只需要知道做三件事
1.定义参数----> 状态、传递个监听者的值
2.监听者的逻辑处理
3.找到发布事件的地方,发布事件即可
以下博客可以参考:
https://blog.csdn.net/qq_23980427/article/details/53443349
https://www.jianshu.com/p/6020dffeceed
https://www.cnblogs.com/aoguren/p/4680692.html