它定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
什么是观察者模式
又称为发布-订阅(Publish/Subscribe)模式。在通知者中提供一个东西(列表、委托),让观察者去注册这个东西,通知者状态改变时,会自动调用观察者中的方法。
当一个对象(通知者)的状态发生改变时,所有依赖于它的对象(观察者)都得到通知并被自动更新(执行各自的操作)
比如,烧水(通知者)时水烧开了,警报器(观察者1)发出警报,显示器(观察者2)显示温度。
再比如,猫叫了(通知者), 耗子跑了(观察者1),大人醒了(观察者2),小孩哭了(观察者3)
游戏中点击开始按钮(通知者状态改变),进行音乐加载、场景加载、加载资源(资源管理器、场景管理器、音乐管理器就是观察者)
为什么使用观察者模式?
为了将观察目标与观察者解耦。
当使用观察者模式后,观察者只需要知道观察目标发生了改变,而不需要知道究竟发生了什么改变。同理,观察目标只需要发出通知,让所有观察者自行做出响应。
示例:猫叫
夜黑风高的晚上
1、一只猫叫了
2、狗也叫了
3、小孩儿哭了
4、哥哥也哭了
5、妈妈起床了
...,面向对象的思想模拟上述过程
scss
public class Cat
{
public void Miao()
{
Console.WriteLine("{0} Miao", GetType().Name);
new Dog().Wang();
new Baby().Cry();
new Brother().Turn();
new Mother().Whisper();
new Father().Roar();
//....
}
}
问题:
1.依赖太重,这只猫不纯粹,包含了不属于他的行为。
2.不好控制顺序,如果修改顺序,只能修改代码,随便修改代码,非常忌讳。
如何解决不稳定?锅还是甩给别人
观察者模式实现
1、定义一个接口
只是为了把多个对象产生关系,方便保存和调用。方法本身其实没用
csharp
public interface IObserver
{
void Action();
}
2、各个实体类实现接口
把不属于猫的动作打个包,丢出去,交给上端指定,保证Cat类稳定
csharp
public class Cat : IObserver
{
public void Miao()
{
Console.WriteLine("{0} Miao", GetType().Name);
}
/// <summary>
/// 观察者模式---通过甩锅来完成;
/// </summary>
public List<IObserver> observerList = new List<IObserver>();
public void MiaoObserver()
{
Console.WriteLine("{0} MiaoObserver", GetType().Name);
foreach (var observer in observerList)
{
observer?.Action();
}
}
public void Action()
{
Miao();
}
}
上端调用
ini
{
Cat cat = new Cat();
cat.Miao();
}
{
//交给上端指定---保证了Cat的稳定;
Cat cat = new Cat();
cat.observerList.Add(new Dog());
cat.observerList.Add(new Baby());
cat.observerList.Add(new Brother());
cat.observerList.Add(new Mother());
cat.observerList.Add(new Father());
cat.MiaoObserver();
}
观察者模式使用步骤
观察者
1、定义一个观察者接口,接口中包含收到通知时的响应方法
csharp
public interface Observer {
void OnNotify();
}
2、定义观察者接口的实现类
arduino
//实现类1
public class ConcreteObserver1 : Observer {
public void OnNotify() {
Console.WriteLine("OnNotify ConcreteObserver1");
}
}
//实现类2
public class ConcreteObserver2 : Observer {
public void OnNotify() {
Console.WriteLine("OnNotify ConcreteObserver2");
}
}
通知者
1、定义通知者抽象类,包含观察者列表及添加、删除观察者的方法,通知观察者的方法
csharp
public abstract class Subject {
// 观察者列表
protected List<Observer> observers;
// 添加观察者
public abstract void Add(Observer observer);
// 删除观察者
public abstract void Remove(Observer observer);
// 通知所有观察者
public abstract void NotifyAllObservers();
}
2、定义通知者的实现类
csharp
public class ConcreteSubject : Subject {
public ConcreteSubject() {
observers = new List<Observer>();
}
public override void Add(Observer observer) {
if (!observers.Contains(observer)) {
observers.Add(observer);
}
}
public override void Remove(Observer observer) {
if (observers.Contains(observer)) {
observers.Remove(observer);
}
}
public override void NotifyAllObservers() {
foreach (Observer o in observers) {
o.OnNotify();
}
}
}
上端调用
scss
// 实例化通知者
Subject subject = new ConcreteSubject();
//实例化观察者
Observer observer1 = new ConcreteObserver1();
Observer observer2 = new ConcreteObserver2();
//向通知者的观察者列表中添加对象
subject.Add(observer1);
subject.Add(observer2);
//通知全体观察者
subject.NotifyAllObservers();
委托实现观察者模式
通知者中
csharp
public Action catCome;
public void CatComing() {
Console.WriteLine($"{Color}的猫{Name}过来了,喵喵喵");
if (catCome != null) {
catCome();
}
}
上端调用
ini
Cat cat = new Cat("加菲猫","黄色");
Mouse mouse1 = new Mouse("米奇", "黑色");
Mouse mouse2 = new Mouse("唐老鸭", "红色");
Mouse mouse3 = new Mouse("xx", "红色");
cat.catCome += mouse1.RunAway;//注册事件
cat.catCome += mouse2.RunAway;
cat.catCome += mouse3.RunAway;
cat.CatComing();
优化
在观察者的构造方法中注册
ini
public Mouse(string name, string color,Cat cat) {
Name = name;
Color = color;
cat.catCome += this.RunAway;
}
外部使用更简单
ini
public void Method() {
Cat cat = new Cat("加菲猫","黄色");
Mouse mouse1 = new Mouse("米奇", "黑色", cat);
Mouse mouse2 = new Mouse("唐老鸭", "红色", cat);
Mouse mouse3 = new Mouse("xx", "红色", cat);
cat.CatComing();
}
优化2:采用事件
事件相当于发布了一个消息,在观察者类的构造方法中,订阅消息
声明事件和声明一个委托使用上完全一样,区别在于只采用委托,可以在外部去调用,比较危险。而事件不能再类的外部触发,只能在类的内部触发,但仍然可以在外界注册。事件本质是一个委托
通知者中
csharp
public event Action catCome;//声明一个事件
外部不能直接调用委托
scss
//直接调用委托会报错
cat.catCome();