观察者模式

它定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。

什么是观察者模式

又称为发布-订阅(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();
相关推荐
卷福同学5 小时前
不用服务器,不用配环境,我10分钟上线了一个AI Agent
人工智能·后端·算法
GetcharZp8 小时前
只需 10 分钟,轻松实现异地组网!Netmaker 保姆级部署教程来了
后端
前端兰博8 小时前
03-Spring Boot
后端
前端兰博8 小时前
01-Java开发基础语言
后端
前端兰博8 小时前
02-Servlet、JDBC、Maven、Mybatis
后端
咖啡八杯9 小时前
GoF设计模式——模板方法模式
java·后端·spring·设计模式
码事漫谈10 小时前
Loop 正在排斥人为操作
后端
爱勇宝10 小时前
办公资料反复修改、补传、交接混乱,我做了个桌面工具来解决这件事
前端·后端·程序员
庄周de蝴蝶10 小时前
梅开二度,这次拿下系统分析师
后端·程序员
林小帅12 小时前
NestJS v11 + Prisma v7 ESM 迁移配置解析
前端·javascript·后端