观察者模式

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

什么是观察者模式

又称为发布-订阅(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();
相关推荐
2401_857439695 分钟前
SpringBoot框架在资产管理中的应用
java·spring boot·后端
怀旧6666 分钟前
spring boot 项目配置https服务
java·spring boot·后端·学习·个人开发·1024程序员节
阿华的代码王国27 分钟前
【SpringMVC】——Cookie和Session机制
java·后端·spring·cookie·session·会话
德育处主任Pro1 小时前
『Django』APIView基于类的用法
后端·python·django
哎呦没3 小时前
SpringBoot框架下的资产管理自动化
java·spring boot·后端
2401_857600953 小时前
SpringBoot框架的企业资产管理自动化
spring boot·后端·自动化
NiNg_1_2347 小时前
SpringBoot整合SpringSecurity实现密码加密解密、登录认证退出功能
java·spring boot·后端
Chrikk9 小时前
Go-性能调优实战案例
开发语言·后端·golang
幼儿园老大*9 小时前
Go的环境搭建以及GoLand安装教程
开发语言·经验分享·后端·golang·go
canyuemanyue9 小时前
go语言连续监控事件并回调处理
开发语言·后端·golang