设计模式第八讲:观察者模式和中介者模式详解

本文涉及的产品
云原生数据库 PolarDB 分布式版,标准版 2核8GB
云原生数据库 PolarDB PostgreSQL 版,企业版 4核16GB
推荐场景:
HTAP混合负载
云原生数据库 PolarDB MySQL 版,Serverless 5000PCU 100GB
简介:  定义一个中介对象来封装一系列对象之间的交互,使原有对象之间的耦合松散,且可以独立地改变它们之间的交互。中介者模式又叫调停模式,它是迪米特法则的典型应用。

一. 观察者模式

  1. 背景
      在现实世界中,许多对象并不是独立存在的,其中一个对象的行为发生改变可能会导致一个或者多个其他对象的行为也发生改变。例如,某种商品的物价上涨时会导致部分商家高兴,而消费者伤心;还有,当我们开车到交叉路口时,遇到红灯会停,遇到绿灯会行。这样的例子还有很多,例如,股票价格与股民、微信公众号与微信用户、气象局的天气预报与听众、小偷与警察等。

  在软件世界也是这样,例如,Excel 中的数据与折线图、饼状图、柱状图之间的关系;MVC 模式中的模型与视图的关系;事件模型中的事件源与事件处理者。所有这些,如果用观察者模式来实现就非常方便。

  1. 定义和特点
    (1). 定义

 指多个对象间存在一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。这种模式有时又称作发布-订阅模式、模型-视图模式,它是对象行为型模式。

PS: 观察者模式是 【1对多】的关系,后面的中介者模式是 【多对多】的关系。

(2). 优点:

 A. 降低了目标与观察者之间的耦合关系,两者之间是抽象耦合关系。

 B. 目标与观察者之间建立了一套触发机制。

(3). 缺点:

 A. 目标与观察者之间的依赖关系并没有完全解除,而且有可能出现循环引用。

 B. 当观察者对象很多时,通知的发布会花费很多时间,影响程序的效率。

  1. 具体实现
    (1). 模式结构

 实现观察者模式时要注意具体目标对象和具体观察者对象之间不能直接调用,否则将使两者之间紧密耦合起来,这违反了面向对象的设计原则。

 A. 抽象目标类:它提供了一个用于保存观察者对象的聚集类和增加、删除观察者对象的方法,以及通知所有观察者的抽象方法。

 B. 具体目标类:也叫具体目标类,它实现抽象目标中的通知方法,当具体主题的内部状态发生改变时,通知所有注册过的观察者对象。

 C. 抽象观察者:它是一个抽象类或接口,它包含了一个更新自己的抽象方法,当接到具体主题的更改通知时被调用。

 D. 具体观察者:实现抽象观察者中定义的抽象方法,以便在得到目标的更改通知时更新自身的状态。

结构图如下:

(2). 使用场景

  老师向学生发通知,老师是具体的目标类,学生是具体的观察者。

(3). 代码实操

抽象观察者和具体的观察者(学生类)

/// <summary>
/// 抽象观察者
/// </summary>
public interface IObserver
{

   /// <summary>
   /// 接收通知
   /// </summary>
   /// <param name="msg"></param>
  void  ReceiveNotice(string msg);

}
 /// <summary>
/// 学生1
/// (具体观察者)
/// </summary>
public class StudentObserver1 : IObserver
{
    public void ReceiveNotice(string msg)
    {
        Console.WriteLine($"学生1已经收到通知,内容为:{msg}");
    }
}
 /// <summary>
/// 学生2
/// (具体观察者)
/// </summary>
public class StudentObserver2 : IObserver
{
    public void ReceiveNotice(string msg)
    {
        Console.WriteLine($"学生2已经收到通知,内容为:{msg}");
    }
}

抽象目标类和具体目标类(老师类)

///
/// 抽象目标
/// (定义操作观察者的相关方法)
///
public abstract class AbstractSub
{
protected List obList = new List();

    /// <summary>
    /// 增加观察者
    /// </summary>
    /// <param name="ob"></param>
    public void AddOb(IObserver ob)
    {
        if (!obList.Contains(ob))
        {
            obList.Add(ob);
        }

    }

    /// <summary>
    /// 删除观察者
    /// </summary>
    /// <param name="ob"></param>
    public void RemoveOb(IObserver ob)
    {
        if (obList.Contains(ob))
        {
            obList.Remove(ob);
        }       
    }

    /// <summary>
    /// 通知具体观察者的方法
    /// </summary>
    public abstract void notifyObserver(string msg);

}

/// <summary>
/// 老师1
/// 具体目标(发通知  发布者)
/// </summary>
public class TeacherSub1 : AbstractSub
{
    /// <summary>
    /// 实现通知学生的业务
    /// </summary>
    public override void notifyObserver(string msg)
    {
        foreach (var item in obList)
        {
            //向每个学生(即观察者)发送通知
            //当观察者较多的时候,此处考虑使用线程池
            item.ReceiveNotice(msg);
        }
    }
}

测试

        {
            //1. 声明所有学生类(观察者)
            IObserver student1 = new StudentObserver1();
            IObserver student2 = new StudentObserver2();

            //2. 声明老师类(发布者)
            AbstractSub teacher1 = new TeacherSub1();
            teacher1.AddOb(student1);
            teacher1.AddOb(student2);

            //3.发送通知
            teacher1.notifyObserver("2020-08-13, 晚上8点全体开会哦");
        }

运行结果

  1. 适用场景分析
    (1). 对象间存在一对多关系,一个对象的状态发生改变会影响其他对象。

(2). 当一个抽象模型有两个方面,其中一个方面依赖于另一方面时,可将这二者封装在独立的对象中以使它们可以各自独立地改变和复用。

更多C++后端开发技术点知识内容包括C/C++,Linux,Nginx,ZeroMQ,MySQL,Redis,MongoDB,ZK,流媒体,音视频开发,Linux内核,TCP/IP,协程,DPDK多个高级知识点。

【文章福利】另外还整理一些C++后台开发架构师 相关学习资料,面试题,教学视频,以及学习路线图,免费分享有需要的可以点击 C++后端学习资料 免费领取

二. 中介者模式

  1. 背景
      在现实生活中,常常会出现好多对象之间存在复杂的交互关系,这种交互关系常常是“网状结构”,它要求每个对象都必须知道它需要交互的对象。例如,每个人必须记住他(她)所有朋友的电话;而且,朋友中如果有人的电话修改了,他(她)必须告诉其他所有的朋友修改,这叫作“牵一发而动全身”,非常复杂。

  如果把这种“网状结构”改为“星形结构”的话,将大大降低它们之间的“耦合性”,这时只要找一个“中介者”就可以了。如前面所说的“每个人必须记住所有朋友电话”的问题,只要在网上建立一个每个朋友都可以访问的“通信录”就解决了。这样的例子还有很多,例如,你刚刚参力口工作想租房,可以找“房屋中介”;或者,自己刚刚到一个陌生城市找工作,可以找“人才交流中心”帮忙。

  在软件的开发过程中,这样的例子也很多,例如,在 MVC 框架中,控制器(C)就是模型(M)和视图(V)的中介者;还有大家常用的 QQ 聊天程序的“中介者”是 QQ 服务器。所有这些,都可以采用“中介者模式”来实现,它将大大降低对象之间的耦合性,提高系统的灵活性。

  1. 定义和特点
    (1). 定义

 定义一个中介对象来封装一系列对象之间的交互,使原有对象之间的耦合松散,且可以独立地改变它们之间的交互。中介者模式又叫调停模式,它是迪米特法则的典型应用。

PS:中介者模式是 【多对多】的关系,上面的观察者模式是 【1对多】的关系。

(2). 优点

 A. 降低了对象之间的耦合性,使得对象易于独立地被复用。

 B. 将对象间的多对多关联转变为一对一的关联,提高系统的灵活性,使得系统易于维护和扩展。

(3). 缺点

 同事类太多时,中介者的职责将很大,它会变得复杂而庞大,以至于系统难以维护

  1. 具体实现
    (1). 模式结构

 A. 抽象中介者:它是中介者的接口,提供了同事对象注册与转发同事对象信息的抽象方法。

 B. 具体中介者:实现中介者接口,定义一个 List 来管理同事对象,协调各个同事角色之间的交互关系,因此它依赖于同事角色。

 C. 抽象同事类:定义同事类的接口,保存中介者对象,提供同事对象交互的抽象方法,实现所有相互影响的同事类的公共功能。

 D. 具体同事类:是抽象同事类的实现者,当需要与其他同事对象交互时,由中介者对象负责后续的交互。

结构图如下:

(2). 使用场景

  一个公司中,不同部门的员工不允许直接对接,这个时候需要一个中间人,帮着沟通联系,这个中间人就是中介者。

(3). 代码实操

抽象中介者和具体中介者

///
/// 抽象中介者
///
public abstract class AbstractMediator
{
//添加同事
public abstract void AddColleague(AbstractColleague colleague);

    //消息转发
    public abstract void TransMsg(AbstractColleague cl,string msg); //转发
}

///
/// 具体中介者
/// (用于协调转发各部门同事的信息传输)
///
public class Mediator1 : AbstractMediator
{
private List cList = new List();

    /// <summary>
    /// 添加同事
    /// </summary>
    /// <param name="colleague"></param>
    public override void AddColleague(AbstractColleague colleague)
    {
        if (!cList.Contains(colleague))
        {
            cList.Add(colleague);
        }
    }

    /// <summary>
    /// 消息转发(1对1)
    /// </summary>
    /// <param name="cl"></param>
    /// <param name="msg"></param>
    public override void TransMsg(AbstractColleague cl, string msg)
    {
        foreach (var item in cList)
        {
            if (item.Equals(cl))
            {
                item.ReceiveMsg(msg);
            }
        }
    }
}

抽象同事类和具体同事类

///
/// 抽象同事类
///
public abstract class AbstractColleague
{
protected AbstractMediator _abstractMediator;

    public AbstractColleague(AbstractMediator abstractMediator)
    {
        this._abstractMediator = abstractMediator;
    }

    /// <summary>
    /// 收消息
    /// </summary>
    /// <param name="msg"></param>
    public abstract void ReceiveMsg(string msg);

    /// <summary>
    /// 发消息(这里指1对1)
    /// </summary>
    /// <param name="msg"></param>
    public abstract void SendMsg(AbstractColleague c, string msg);

}

///
/// 同事1(位于业务部门)
///
public class Colleague1 : AbstractColleague
{
public Colleague1(AbstractMediator meditor):base(meditor)
{

    }

    public override void ReceiveMsg(string msg)
    {
        Console.WriteLine($"同事1收到新消息,内容为:{msg}");
    }

    public override void SendMsg(AbstractColleague c, string msg)
    {
        //调用中介者进行消息转发
        _abstractMediator.TransMsg(c, msg);
    }
}

///
/// 同事2(位于技术部门)
///
public class Colleague2 : AbstractColleague
{
public Colleague2(AbstractMediator meditor) : base(meditor)
{

    }

    public override void ReceiveMsg(string msg)
    {
        Console.WriteLine($"同事2收到新消息,内容为:{msg}");
    }

    public override void SendMsg(AbstractColleague c, string msg)
    {
        //调用中介者进行消息转发
        _abstractMediator.TransMsg(c, msg);
    }
}

///
/// 同事3,位于行政部门
///
public class Colleague3 : AbstractColleague
{
public Colleague3(AbstractMediator meditor) : base(meditor)
{

    }

    public override void ReceiveMsg(string msg)
    {
        Console.WriteLine($"同事3收到新消息,内容为:{msg}");
    }

    public override void SendMsg(AbstractColleague c, string msg)
    {
        //调用中介者进行消息转发
        _abstractMediator.TransMsg(c, msg);
    }
}

测试

{
//1.声明中介者
AbstractMediator aMediator = new Mediator1();

            //2. 声明各业务同事
            AbstractColleague c1 = new Colleague1(aMediator);
            AbstractColleague c2 = new Colleague2(aMediator);
            AbstractColleague c3 = new Colleague3(aMediator);

            //3.给中介者添加各个同事
            aMediator.AddColleague(c1);
            aMediator.AddColleague(c2);
            aMediator.AddColleague(c3);

            //4.开始信息交流
            Console.WriteLine("c1发消息给c2");
            c1.SendMsg(c2, "今晚一起吃饭吧");

            Console.WriteLine("c2发消息给c3");
            c3.SendMsg(c3, "明天8点一起开会");

}
运行结果

  1. 适用场景分析
    (1). 当对象之间存在复杂的网状结构关系而导致依赖关系混乱且难以复用时。

(2). 当想创建一个运行于多个类之间的对象,又不想生成新的子类时。

相关文章
|
13天前
|
设计模式 安全 Java
Java面试题:设计模式如单例模式、工厂模式、观察者模式等在多线程环境下线程安全问题,Java内存模型定义了线程如何与内存交互,包括原子性、可见性、有序性,并发框架提供了更高层次的并发任务处理能力
Java面试题:设计模式如单例模式、工厂模式、观察者模式等在多线程环境下线程安全问题,Java内存模型定义了线程如何与内存交互,包括原子性、可见性、有序性,并发框架提供了更高层次的并发任务处理能力
28 1
|
22天前
|
设计模式 JavaScript
js设计模式【详解】—— 中介者模式
js设计模式【详解】—— 中介者模式
35 5
|
28天前
|
设计模式
中介者模式-大话设计模式
中介者模式-大话设计模式
17 1
|
1月前
|
设计模式 消息中间件 存储
跟着GPT学设计模式之观察者模式
观察者模式是一种行为型设计模式,它定义了对象之间的一对多依赖关系,使得当一个对象的状态发生改变时,其依赖对象都能够收到通知并自动更新。一般情况下,被依赖的对象叫作被观察者(Observable),依赖的对象叫作观察者(Observer)。
18 1
|
28天前
|
设计模式 Java 程序员
Java设计模式之中介者模式详解
Java设计模式之中介者模式详解
|
28天前
|
设计模式 存储 Java
Java设计模式之观察者模式详解
Java设计模式之观察者模式详解
|
28天前
|
设计模式
观察者模式-大话设计模式
观察者模式-大话设计模式
9 0
|
1月前
|
设计模式
设计模式之中介者模式
设计模式之中介者模式
|
1月前
|
设计模式 存储
行为型设计模式之观察者模式
行为型设计模式之观察者模式
|
2月前
|
设计模式 Java
Java一分钟之-设计模式:观察者模式与事件驱动
【5月更文挑战第17天】本文探讨了Java中实现组件间通信的观察者模式和事件驱动编程。观察者模式提供订阅机制,当对象状态改变时通知所有依赖对象。然而,它可能引发性能问题、循环依赖和内存泄漏。代码示例展示了如何实现和避免这些问题。事件驱动编程则响应用户输入和系统事件,但回调地狱和同步/异步混淆可能造成困扰。JavaFX事件驱动示例解释了如何处理事件。理解这两种模式有助于编写健壮的程序。
37 1