首页 > 其他分享 >中介者模式:如何通过中间层来解决耦合过多的问题?

中介者模式:如何通过中间层来解决耦合过多的问题?

时间:2024-09-21 17:51:17浏览次数:9  
标签:String mediator 对象 void 中介 耦合 中间层 public

中介者模式理解起来并不难,代码实现简单,学习难度也很小,只要合理充分地应用这个模式,往往就能够解决一些意想不到的问题。那这到底是怎样一个模式?多用于什么场景中?为什么使用?该怎么使用?下面我们一起来看看吧。

一、模式原理分析

中介者模式的原始定义是:中介者对象封装了一组对象之间的交互,这组对象会将它们的交互委托给中介者对象,而不是直接交互。

可以看到,这个定义是难得的简单和明确,中介者对象就是用于处理对象与对象之间的直接交互,封装了多个对象之间的交互细节。

我们还是先来看看中介者模式的 UML 图:

从这个 UML 图中,我们能看出中介者模式包含了四个关键角色。

  • 抽象中介者(Mediator):定义中介者需要执行的方法操作。

  • 具体中介者(MediatorImpl):实现抽象中介者定义的方法操作,同时可以包含更多逻辑。

  • 抽象组件类(Component):定义组件需要执行的方法操作。

  • 具体组件类(ComponentA、ComponentB):继承自抽象组件类,实现具体的组件业务逻辑。

下面我们再来看看 UML 对应的代码实现:

public interface Mediator {
    void apply(String key);
}
public class MediatorImpl implements Mediator{
    @Override
    public void apply(String key) {
        System.out.println("最终中介者执行操作,key为:"+key);
    }
}
public abstract class Component {
    private Mediator mediator;
    public Component(Mediator mediator) {
        this.mediator = mediator;
    }
    public abstract void exec(String key);
    public Mediator getMediator() {
        return mediator;
    }
}
public class ComponentA extends Component{
    public ComponentA(Mediator mediator) {
        super(mediator);
    }
    @Override
    public void exec(String key) {
        System.out.println("===在组件A中,通过中介者执行");
        getMediator().apply(key);
    }
}
public class ComponentB extends Component{
    public ComponentB(Mediator mediator) {
        super(mediator);
    }
    @Override
    public void exec(String key) {
        System.out.println("===在组件B中,通过中介者的操作");
        getMediator().apply(key);
    }
}
public class Demo {
    public static void main(String[] args) {
        Mediator mediator = new MediatorImpl();
        Component componentA = new ComponentA(mediator);
        componentA.exec("key-A");
        Component componentB = new ComponentB(mediator);
        componentB.exec("key-B");
    }
}
//输出结果
===在组件A中,通过中介者执行
最终中介者执行操作,key为:key-A
===在组件B中,通过中介者的操作
最终中介者执行操作,key为:key-B

从上面的代码实现中,可以发现中介者模式的关键点就在于在组件与组件之间加入一个中间对象来进行间接通信。虽然多了“一层”会更烦琐些,但是这样就可以在中介者里进行其他的一些操作。

二、使用场景分析

中介者模式常见的使用场景有以下几种:

  • 系统中对象之间存在复杂的引用关系时,比如,聊天系统;

  • 通过一个中间对象来封装多个类中的共有行为时,比如,在分层架构中的 DAO 层和数据库 DB 层中间再引入一个读写分离和读写均衡的中间层;

  • 不想生成太多的子类时。

为了快速理解中介者的适用场景,下面我们还是通过一个简单的例子来讲解和演示。

假设我们要设计一个可以让多人参与进去的聊天室,该怎么去实现呢?首先,定义聊天室的接口 ChatRoom,其中包含两个方法 sendMessage 和 addUser,分别代表发送消息和新增用户。这里的 ChatRoom 就是抽象的中介者类。

public interface ChatRoom {
    void sendMessage(String msg, String userId);
    void addUser(User user);
}

然后,创建一个 ChatRoom 的实现类ChatRoomImpl,使用 addUser 来添加需要聊天的用户对象,同时这里再使用一个Map来保存添加时需要用来进行通信的对象列表,在发送消息 sendMessage 的方法中,我们通过 userId 指定某个对象来接收消息。

public class ChatRoomImpl implements ChatRoom {
    private Map<String, User> usersMap = new HashMap<>();
    @Override
    public void sendMessage(String msg, String userId) {
        User u = usersMap.get(userId);
        u.receive(msg);
    }
    @Override
    public void addUser(User user) {
        this.usersMap.put(user.getId(), user);
    }
}

接下来我们再定义一个抽象组件类 User,如下所示:

public abstract class User {
    private ChatRoom mediator;
    private String id;
    private String name;
    public User(ChatRoom room, String id, String name){
        this.mediator = room;
        this.name = name;
        this.id = id;
    }
    public abstract void send(String msg, String userId);
    public abstract void receive(String msg);
    public ChatRoom getMediator() {
        return mediator;
    }
    public String getId() {
        return id;
    }
    public String getName() {
        return name;
    }
}

继承User实现一个具体的组件类 ChatUser,并实现发送消息 send 和接收消息 receive 的方法。

public class ChatUser extends User {
    public ChatUser(ChatRoom room, String id, String name) {
        super(room, id, name);
    }
    @Override
    public void send(String msg, String userId) {
        System.out.println(this.getName() + " :: Sending Message : " + msg);
        getMediator().sendMessage(msg, userId);
    }
    @Override
    public void receive(String msg) {
        System.out.println(this.getName() + " :: Received Message : " + msg);
    }

}

最后,我们同样还是运行一段单元测试代码:

public class Client {
    public static void main(String[] args) {
        ChatRoom chatroom = new ChatRoomImpl();
        User user1 = new ChatUser(chatroom,"1", "Spike");
        User user2 = new ChatUser(chatroom,"2", "Mia");
        User user3 = new ChatUser(chatroom,"3", "Max");
        User user4 = new ChatUser(chatroom,"4", "Mick");
        chatroom.addUser(user1);
        chatroom.addUser(user2);
        chatroom.addUser(user3);
        chatroom.addUser(user4);
        user1.send("Hello man", "2");
        user2.send("Hey", "1");
    }
}
//输出结果
Spike :: Sending Message : Hello man
Mia :: Received Message : Hello man
Mia :: Sending Message : Hey
Spike :: Received Message : Hey

到此,我们就完成了一个简单的聊天室程序。从代码实现中能看出,中介者在使用时需要知道对象之间的交互关系,然后通过封装这些交互关系的变化让对象在使用中介者时变得更简单

三、为什么使用中介者模式?

分析完中介者模式的原理和使用场景后,我们再来说说使用中介者模式的原因,可总结为以下三个。

第一个,解决对象之间直接耦合的问题,避免“一处修改多处”的连锁反应出现。比如,在上面聊天室的例子中,如果用户与用户之间是直连通信的,那么任何一个用户对象发生变化都会影响到直接聊天的那个用户,而那个用户可能又在跟别的用户聊天,以此类推,用户之间的关系会变得越来越复杂。这样,当我们再修改代码时,就很容易造成“一处修改又引起多处修改”的连锁反应。而使用中介者模式时,用户会通过聊天室和别的用户通信,避免了直接与对方通信,这样修改某个用户对象时并不会影响到其他对象。

第二个,在结构上作为中转,解耦两个服务或系统之间的直接耦合关系。在分层架构中,我们都知道视图层一般不会直接使用 DAO 层,因为一旦直接使用,DAO 层任何一个微小的变动都可能引起视图层的变化,这时通常会引入 Service 层作为中介者来进行请求的转发,以达到解耦的目的,避免了相互之间的直接影响,同时也能在中间层里加入一些特定的逻辑,如性能监控、埋点数据记录等。

第三个,为了更便捷地统一协同对象之间的通信。我们知道在对远程服务器进行调用时,协调网络通信是一件异常复杂和烦琐的事情,这时如果有一个中介者来统一协调,则会大大提升效率,比如,Dubbo 一类的 RPC 框架就是一个完整的中介者模式的体现。对于所有的 Java RPC 调用来说,只需要通过这个中间层来进行通信即可,而不需要知道对方服务器地址以及如何发起网络调用。

四、中介者模式的优缺点是什么?

通过以上分析,我们可以得出中介者模式主要有这样几个优点。

  • 减少对象之间的直接交互,间接解耦过多依赖。比如,Maven 就是 Java 中引用 jar 包时的中介者,如果我们手动直接引用 jar 包,会容易造成非常混乱的引用关系,而使用 Maven 则能很方便地减少代码直接依赖 jar 包的问题。

  • 减少子类的创建数量。比如,在多个用户的会话请求中,我们可以使用一个通用的上下文的中介者来保存会话中一些不变的静态数据,这样就不需要每新增一个会话都需要新增一些静态数据。

  • 简化各系统的设计和实现。由于中介者能够处理一些共用的通信逻辑,所以其他对象在进行自身业务的处理时可以不用关心共用的通信逻辑,这样就大大减少了系统的实现逻辑。

  • 通过新建中间层快速扩展新功能,提升代码扩展性。比如,对象通过中间层调用时,我们可以在中间层加入对每一次请求或方法调用的耗时统计,这样就能快速扩展功能。

同样,中介者模式也有一些缺点。

  • 中介者类中的交互逻辑可能变得非常复杂且难以维护。当中介者类中包含了太多对象之间的交互细节后,中介者就变成了新的复杂对象,使得系统维护成本变高。

  • 中介者变成了新的重度依赖对象。一旦中介者对象变得复杂后,势必会增加与其他对象之间的耦合度,而这时如果中介者对象发生故障,则依赖的相关对象也会受到影响,修改中介者也会影响关联对象。

  • 中介者需要知道所有对象交互的逻辑。由于中介者对象承担了交互对象的传输渠道,所以就需要知道对象交互的详细细节,这样无疑增加了中介者对象的学习成本。

总结来讲,中介者模式提供了一种减少对象之间耦合度的思路。对于一些维护性的旧项目来说,直接修改已有代码通常都会导致系统出现问题,而通过引入中间层,能够起到过渡的作用,同时还能够逐渐解耦原有的强耦合关系,让系统的扩展性变得更强。不过,中介者也可能因此变得异常复杂,一旦中介者出现问题,就会导致所有系统都出现问题,所以在使用时也需要注意设计的度。

文章(专栏)将持续更新,欢迎关注公众号:服务端技术精选。欢迎点赞、关注、转发

标签:String,mediator,对象,void,中介,耦合,中间层,public
From: https://blog.51cto.com/jiangyi/12075023

相关文章

  • 台球瞄准的投掷效应或者耦合效应
    https://www.zhihu.com/question/27659022怪不得理论上算的角度,实际上打总是便宜,原来这里面还有两个球之间的摩擦.也就是:老师,这是您八年前的提问。我个人理解是:目标球会跟着主球往同一个方向走。打个比喻就是“目标球”会坐上“主球”的“火车”,“在短暂的相遇中,同行一段路......
  • ssm基于SSM框架的二手房中介管理系统
    系统包含:源码+论文所用技术:SpringBoot+Vue+SSM+Mybatis+Mysql免费提供给大家参考或者学习,获取源码请私聊我需要定制请私聊目录第1章绪论 11.1课题背景 11.2课题意义 11.3研究内容 2第2章开发环境与技术 32.1Java语言 32.2MYSQL数据库 32.3IDEA开发......
  • 软件工程中的耦合:类型、影响与优化策略
    目录1.耦合的类型2.耦合的影响3.降低耦合的策略在软件工程中,耦合是指不同模块、组件或系统之间的相互依赖程度。耦合的概念是模块化设计的核心,它直接影响到软件的可维护性、可扩展性和可测试性。本文将详细介绍耦合的类型、其对软件项目的影响,以及如何通过设计策略来降......
  • 中介者模式
    中介者模式中介者模式(MediatorPattern)是一种行为型设计模式,用于减少多个对象或类之间复杂的交互依赖。通过引入一个中介者对象,所有对象之间的通信不再直接进行,而是通过中介者来协调和管理,从而降低对象之间的耦合度。这使得系统更易于扩展和维护,因为对象之间的关系可以通过修改中......
  • 短视频开源代码,微前端助力系统达成松耦合
    短视频开源代码,微前端助力系统达成松耦合什么是微前端?微前端是一种网络开发架构模式,将微服务的原则扩展到网络应用的前端。它涉及将网络应用的用户界面分解为更小的、松散耦合的、可以独立部署的前端模块。每个模块代表应用的一个独特功能或特性,可以独立开发、测试和部署。微前......
  • 【看来我要63岁才能退休了】超简单!低耦合!一步在自己PyQt5、PySide6界面中加入文件资源
    【......
  • 【使用 3D FDTD 代码和 UPML 进行微带分支线耦合器分析】三维有限差分时域方法在平面
       ......
  • 微波无源器件 4 基于高阶定向耦合器的双极化波束形成网络
    摘要:    一种Ka频段的双极化3dB定向耦合器被设计用于波束形成网络应用。所提出的解决方案对于紧凑Nolen网络。Nolen结构优于器平面和无损特别具有吸引力。两个平行方波导通过口径阵列耦合,设计用于获得两个正交极化之间的所需耦合和高隔离度。索引词:    阵列......
  • ADCIRC+SWAN耦合模型使用流程
    0、前言        由于我发现,关于风暴潮耦合模型ADCIRC+SWAN,能够在网上找到的资料实在是少之又少,所以特此打算写一系列的有关该模型的使用流程。1、软件介绍        在这里或许有人会问,到底什么是ADCIRC?什么是SWAN?为什么会有两个模型,而ADCIRC+SWAN又是什么?......
  • 基于GIS、RS、VORS模型、CCDM模型、geodetecto、GWR模型集成的生态系统健康的耦合协调
       将用案例实训,教授如何集成多源数据,依托ArcGISPro和R语言环境,采用“活力-组织力-恢复力-贡献力”(VORS)模型定量测算生态系统健康指数(EHI);如何从经济城镇化(GDPD)、人口城镇化(POPD)和土地城镇化(ULP)构建城镇化指数(UL)测算模型;如何定量测算长时序城镇化水平及生态系统健康状况,利用......