揭秘中介者模式-如何优雅地管理对象间的沟通


声明:本文转载文章只截取部分内容,更详细的文章内容,请跳转原文地址
原文链接:https://blog.csdn.net/danci_/article/details/136367657


一、案例场景🔍

在这里插入图片描述

1.1 经典的运用场景

    中介者模式在软件开发中的多种应用场景。以下是中介者模式在分布式系统、微服务架构和聊天室应用等场景中的经典应用及其优势:👇

  1. 分布式系统:
    在分布式系统中,各个节点(或服务)之间需要进行通信和协作。中介者模式可以应用于此,作为中央协调器,负责管理和调度各个节点之间的通信。

    降低耦合度:通过中介者,各个节点可以只与中介者通信,而无需了解其他节点的具体实现和位置,从而降低了节点之间的耦合度。
    提高可扩展性:新节点加入系统时,只需与中介者进行交互,无需修改其他节点的代码,提高了系统的可扩展性。
    简化复杂性:中介者可以封装复杂的通信逻辑,使得各个节点只需关注自己的业务逻辑,简化了系统的复杂性。
  • 2. 微服务架:
    在微服务架构中,服务之间需要进行通信和协作。中介者模式可以作为服务网关或服务注册中心的角色出现,负责服务的发现、路由和负载均衡。

    服务解耦:通过中介者(如服务网关),服务之间可以实现解耦,各自独立演化,提高了系统的可维护性。
    统一入口:中介者可以作为系统的统一入口,对外部请求进行身份验证、限流、熔断等处理,提高了系统的安全性。
    动态扩展:通过服务注册中心,服务可以动态地注册和发现其他服务,实现了系统的动态扩展。

  • 3. 聊天室应用:
    在聊天室应用中,用户之间需要进行实时的消息传递。中介者模式可以作为消息服务器的角色出现,负责接收、存储和转发用户之间的消息。

    实时性:通过中介者(消息服务器),用户之间可以实现实时的消息传递,提高了用户的体验。
    解耦用户:用户只需与消息服务器通信,无需了解其他用户的具体实现和位置,降低了用户之间的耦合度。
    支持大规模并发:消息服务器可以设计为支持大规模并发连接和消息传递,满足了聊天室应用的高并发需求。

    下面我们来实现分布式系统场景 📄✏️。

1.2 一坨坨代码实现😻

在这里插入图片描述

    不使用中介者模式的情况下实现分布式系统中的节点通信,可以采用直接通信的方式,即每个节点都保存其他节点的引用或地址,并直接与其他节点进行通信。这种方法虽然简单,但随着节点数量的增加,节点之间的连接数将呈指数级增长,导致系统的复杂性和维护成本增加。
    这里提供一个简化的示例,其中只有两个节点(NodeA 和 NodeB)进行通信。在实际应用中,您可能需要使用更复杂的机制,如消息队列、RPC 框架或分布式事件总线来处理节点之间的通信。以下是简单的 Java 示例:👇

// 定义节点接口  
interface Node {  
    void sendMessage(String message, Node recipient);  
    void receiveMessage(String message);  
}  
  
// 实现节点A  
class NodeA implements Node {  
    private NodeB nodeB; // 直接持有NodeB的引用  
  
    public NodeA(NodeB nodeB) {  
        this.nodeB = nodeB;  
    }  
  
    @Override  
    public void sendMessage(String message, Node recipient) {  
        if (recipient instanceof NodeB) {  
            nodeB.receiveMessage(message); // 直接发送给NodeB  
        } else {  
            System.out.println("Unsupported recipient type");  
        }  
    }  
  
    @Override  
    public void receiveMessage(String message) {  
        System.out.println("NodeA received: " + message);  
    }  
}  
  
// 实现节点B  
class NodeB implements Node {  
    @Override  
    public void sendMessage(String message, Node recipient) {  
        // 在这个简化的示例中,NodeB不发送消息给其他节点  
        System.out.println("NodeB is not configured to send messages");  
    }  
  
    @Override  
    public void receiveMessage(String message) {  
        System.out.println("NodeB received: " + message);  
    }  
}  
  
// 主程序入口  
public class DistributedSystemExample {  
    public static void main(String[] args) {  
        NodeB nodeB = new NodeB(); // 创建NodeB实例  
        NodeA nodeA = new NodeA(nodeB); // 创建NodeA实例,并将NodeB的引用传递给它  
        nodeA.sendMessage("Hello from NodeA", nodeB); // NodeA发送消息给NodeB  
    }  
}

    在这个示例中,我们定义了一个 Node 接口,其中包含 sendMessage 和 receiveMessage 方法。然后,我们实现了两个节点类 NodeA 和 NodeB,它们分别实现了 Node 接口。NodeA 持有一个对 NodeB 的引用,并可以直接向其发送消息。然而,这个示例非常简单且有限,因为它只支持两个节点之间的单向通信。
    在实际应用中,可能需要考虑使用更复杂的通信机制来处理多个节点之间的双向通信、错误处理、异步通信等问题。此外,随着节点数量的增加,可能需要引入中介者模式或其他设计模式来降低系统的复杂性并提高可维护性。

    虽然上述实现没有使用设计模式,但也体现出了如下优点:👇

  1. 简单性:
    🚀 代码实现非常直接和简单,容易理解。对于初学者或者小型项目来说,这种简单性可能是一个优点,因为它减少了复杂性并使得代码易于维护。
  2. 直接通信:
    🚀 由于 NodeA 直接持有 NodeB 的引用,因此在两个节点之间传输数据时没有任何中间层,这可以减少延迟和额外的网络开销(在分布式系统的上下文中,这通常不是优点,但在某些特定场景,如紧密集成的组件之间,这可能是可接受的)。
  3. 无需额外依赖:
    🚀 实现没有使用任何外部库或框架,这意味着它可以在没有这些依赖项的环境中运行,减少了部署和管理的复杂性。
  4. 明确的依赖关系:
    🚀 NodeA 对 NodeB 的依赖关系是明确的,这有助于在设计和分析系统时理解组件之间的交互。
  5. 适用于演示和教学:
    🚀 作为一个教学示例,它很好地展示了如何在没有使用设计模式的情况下实现基本的节点间通信。

1.3 痛点

在这里插入图片描述
    然而,没有复杂的设计下体现上述优点的同时也伴随着一些潜在的缺点,比如代码的耦合性、通用性和可扩展性可能会受到限制。对于更大或更复杂的项目,可能需要考虑使用设计模式和其他高级技术来改善代码的结构和质量。
    缺点(问题)👇逐一分析:

  1. 紧耦合:
    😉 NodeA 直接依赖于 NodeB,这导致两者之间的紧密耦合。如果 NodeB 的实现发生变化,或者需要引入新的节点类型,NodeA 的代码可能也需要相应修改。
  2. 扩展性差:
    😉 这个简单的实现不支持多于两个节点的系统。每增加一个节点,都需要在现有的节点类中增加对新节点的引用和处理逻辑,这会导致代码迅速变得复杂且难以维护。
  3. 单向通信:
    😉 在这个实现中,只有 NodeA 能够发送消息给 NodeB,而 NodeB 没有办法回应或者发送消息给其他节点。这在真实的分布式系统中是不够用的。
  4. 缺乏通用性:
    😉 sendMessage 方法中的类型检查限制了只能发送消息给特定类型的节点。这种方法不够灵活,也不支持动态地添加或移除节点。
  5. 缺乏错误处理:
    😉 在发送消息时,没有考虑到可能的错误情况,比如网络故障、接收节点不可用等。
  6. 同步阻塞调用:
    😉 sendMessage 方法是同步的,这意味着发送节点在消息被接收之前会一直等待。在实际的分布式系统中,通常更倾向于使用异步通信来避免阻塞。
  7. 单点故障:
    😉 由于 NodeA 直接依赖于 NodeB,如果 NodeB 出现故障,NodeA 将无法正常工作。

    当考虑到分布式系统的复杂性和可扩展性需求时。以下是一些被违反的设计原则(问题)👇逐一分析:

  1. 开闭原则(Open/Closed Principle):
    💪 软件实体(类、模块、函数等)应当对扩展开放,对修改关闭。在上述实现中,如果我们需要增加新的节点类型或者改变节点之间的通信方式,我们可能需要修改现有的 NodeA 和 NodeB 类的代码,这违背了开闭原则。
  2. 依赖倒置原则(Dependency Inversion Principle):
    💪 高层模块不应该依赖于低层模块,它们都应该依赖于抽象;抽象不应该依赖于细节,细节应该依赖于抽象。在上述实现中,NodeA 直接依赖于 NodeB 的具体实现,而不是依赖于一个更抽象的接口或基类,这违背了依赖倒置原则。
  3. 单一职责原则(Single Responsibility Principle):
    💪 一个类应该只有一个引起变化的原因。在上述实现中,NodeA 既负责自己的业务逻辑,又负责与 NodeB 的通信逻辑,这可能使得 NodeA 的职责过于复杂,违背了单一职责原则。
  4. 里氏替换原则(Liskov Substitution Principle):
    💪 在软件系统中,一个可以接受基类对象的地方,必然可以接受一个子类对象,而不会出现任何异常。虽然这个原则在上述实现中没有直接被违背(因为没有涉及到继承),但是如果我们尝试将 NodeA 和 NodeB 抽象为一个基类,并创建子类来扩展它们,可能会遇到问题,因为子类可能需要覆盖或修改基类的方法以实现特定的通信逻辑。
  5. 接口隔离原则(Interface Segregation Principle):
    💪 客户端不应该依赖它不需要的接口;一个类对另一个类的依赖性应当是最小的。在上述实现中,没有明确地定义接口,但是如果我们尝试引入接口来抽象节点的行为,可能会发现接口包含了太多不必要的方法,或者不同的节点类型需要实现不同的接口,这违背了接口隔离原则。
  6. 迪米特法则(Law of Demeter)或最少知识原则(Least Knowledge Principle):
    💪 一个对象应该对其他对象保持最少的了解。在上述实现中,NodeA 直接与 NodeB 交互,并且了解 NodeB 的具体实现细节(例如,它知道消息是发送给 NodeB 的),这违背了迪米特法则。

二、解决方案🚀

在这里插入图片描述
    为了解决上述代码中的缺点,我们可以使用中介者模式来重构代码。中介者模式引入了一个中介者对象,用于封装节点之间的通信逻辑,从而降低节点之间的耦合度并提高系统的可扩展性。

2.1 定义

简化对象间交互,通过中央协调者管理通信,降低耦合度。

2.2 案例分析🧐

在这里插入图片描述

2.3 中介者模式结构图及说明

在这里插入图片描述



声明:本文转载文章只截取部分内容,更详细的文章内容,请跳转原文地址
原文链接:https://blog.csdn.net/danci_/article/details/136367657


  • 29
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
从clickhouse到bytehouse - 揭秘字节跳动万台节点 字节跳动在数据分析和大数据处理方面一直是行业的领先者。为了应对快速增长的数据需求以及提高数据分析的效率,字节跳动开发了一种高性能的数据存储和查询引擎,名为ClickHouse。ClickHouse是一个开源的列式数据库管理系统,通过利用现代硬件的计算和存储能力,能够高效地处理海量数据。 然而,随着字节跳动用户数量的不断增加,ClickHouse在处理大规模数据时也面临一些挑战。为了进一步提升数据分析的速度和效率,字节跳动决定开发一种名为ByteHouse的新型数据存储和查询引擎。 ByteHouse是在ClickHouse的基础上进行了优化和改进的新一代数据处理引擎。它采用了自主设计的分布式架构,将数据存储和查询任务分发到多台节点上进行并行处理。字节跳动部署了数万台节点,每个节点都具备计算和存储能力,以应对海量数据处理的需求。 ByteHouse引擎的主要特点之一是其高度可扩展性。可以根据数据的增长情况随时扩展节点数量,从而保持系统的稳定性和性能。此外,ByteHouse还采用了多级缓存和数据压缩等技术,以进一步提高数据查询的速度和效率。 字节跳动通过使用ByteHouse引擎,实现了对海量数据的高速查询和实时分析。无论是在搜索、内容推荐还是广告投放等方面,ByteHouse都起到了关键作用。该引擎的高性能和可扩展性,使得字节跳动能够处理复杂的数据分析任务,并提供即时的洞察和决策支持。 总的来说,从ClickHouse到ByteHouse的升级,揭示了字节跳动在大数据处理方面的技术实力和创新能力。通过高性能的数据存储和查询引擎,字节跳动能够处理海量数据,并在多个业务场景中发挥关键作用。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值