本书的原著为:《Design Patterns for Embedded Systems in C ——An Embedded Software Engineering Toolkit 》,讲解的是嵌入式系统设计模式,是一本不可多得的好书。
本系列描述我对书中内容的理解。本文章描述访问硬件的设计模式之三:中介者模式。
中介者模式
(Mediator Pattern) 是一种设计模式,它在软件工程中用于减少类与类之间的直接依赖,降低系统的耦合度,使系统更加灵活和可扩展。该模式通过引入一个中介者对象,将原本需要直接交互的多个对象之间的关系转化为中介者与这些对象之间的关系,从而实现了解耦。
在本书中,中介者模式用于协调复杂交互,使得各个硬件接口不需要显示地相互引用,从而降低耦合度。
为了更好地理解中介者模式,我们可以将其比作现实生活中的房屋中介。在房屋租赁市场中,房东和租客之间通常需要直接进行交互以完成租赁交易。然而,当市场变得复杂时,这种直接交互可能会变得困难且效率低下。此时,房屋中介作为中介者出现,他们负责协调房东和租客之间的交互,处理租赁合同、租金支付等事务,从而简化了交互过程。
在软件工程中,中介者模式同样扮演着类似的角色。例如,在一个图形用户界面(GUI)系统中,多个组件(如按钮、文本框等)之间可能需要进行复杂的交互。如果每个组件都直接与其他组件进行交互,那么系统的耦合度将会非常高,一旦某个组件发生变化,可能会影响到其他多个组件。此时,可以引入一个中介者对象(如事件处理器)来管理这些组件之间的交互。当某个组件需要与其他组件进行交互时,它只需将请求发送给中介者对象,由中介者对象负责协调和处理这些请求。
中介者模式的核心在于将原本复杂的对象间关系转化为简单的中介者与对象间的关系。这种转化不仅降低了系统的耦合度,还提高了系统的可维护性和可扩展性。当需要添加新的交互关系或修改现有交互关系时,只需修改中介者对象即可,而无需修改其他多个对象。
然而,中介者模式也存在一些潜在的问题。例如,如果中介者对象变得过于复杂和庞大,那么它可能会成为系统中的瓶颈。此外,如果系统中存在多个中介者对象,它们之间可能需要进行协调和管理,否则可能会导致系统变得混乱和难以维护。
摘要
通过引入一个中介者对象来封装硬件元素之间交互的复杂性,中介者模式允许硬件元素之间更松散的耦合,提高了代码的可维护性和可扩展性。在C语言中,由于没有直接支持面向对象编程的特性,如类和继承,因此使用中介者模式可以避免因过度使用特殊化而导致的实现复杂性增加。相反,中介者模式可以通过结构体和函数指针等C语言特性来实现,从而简化对硬件元素的管理和协调。
问题
许多嵌入式应用控制一组执行器,这些执行器必须协同工作才能实现预期的效果。例如,为了实现多关节机械臂的协调运动,所有电机必须协同工作以提供所需的机械臂运动。类似地,在三维空间中使用航天器的反作用轮或推进器时,需要许多不同的此类设备,它们要在精确的时刻、提供适当的作用力,才能实现姿态稳定。
在这种情况下,中介者模式可以用来管理这些执行器之间的复杂交互。中介者可以作为一个集中的控制点,接收来自应用程序的指令,并根据需要协调各个执行器的行为。这样可以将执行器之间的直接交互降到最低,使得系统更加模块化和可维护。同时,通过使用中介者模式,我们可以更容易地添加、修改或删除执行器,而不需要对整个系统进行大规模的修改。
模式结构
模式结构图如下图所示:
中介者模式使用一个中介者类来协调一组协作设备的 动作
(actions),以实现所需的整体效果。中介者类协调对多个具体协作者(Specific Collaborator)集合的控制(它们的数量由图3-5中中介者和具体协作者之间的关联上的’*'表示)。当发生感兴趣的事件时,每个具体协作者必须能够联系中介者。
在这种模式中,中介者充当了中央控制器的角色,负责接收和处理来自各个协作者的消息,并根据需要协调它们的行为。这样可以将协作者之间的直接依赖关系降到最低,提高了系统的可维护性和可扩展性。同时,由于中介者负责协调和控制整个系统的行为,因此可以更容易地实现复杂的交互和协作逻辑。
模式详情
协作者接口
协作者接口
(Collaborator Interface)是所有具体协作者共有的一组服务的规范,这些服务可以被中介者调用。例如,对于所有硬件设备来说,通常会有 initialize() 、reset()、shutdown() 等操作,以便将它们都带到一个已知的初始状态、恢复状态、关闭状态。每个通用服务必须在每个具体协作者中实现(当然,实现通常对每个协作者来说是唯一的)。
通过使用协作者接口,我们可以确保中介者能够与所有具体协作者以一致的方式进行交互,而无需关心它们的具体实现细节。这有助于降低系统的复杂性,并提高代码的可维护性和可扩展性。同时,这也为添加新的具体协作者提供了灵活性,因为只要它们实现了协作者接口,就可以与中介者进行交互。
具体协作者
具体协作者
(Specific Collaborator)代表一个设备,因此可能是一个设备驱动程序或硬件代理。它接收中介者的命令消息,并在发生感兴趣的事件时向中介者发送消息。
在中介者模式中,具体协作者通常负责处理与具体设备相关的具体操作和事件。它们与中介者进行交互,通过接收中介者的命令来执行相应的操作,并在必要时向中介者发送事件消息以通知其状态变化或其他重要事件的发生。
通过将设备逻辑封装在具体协作者中,中介者模式实现了设备与系统其他部分之间的解耦。这使得系统更加模块化,易于维护和扩展。同时,具体协作者的设计也使得对设备的控制和管理更加集中和统一,提高了系统的整体效率和可靠性。
中介者
中介者
类(Mediator)在模式中协调 具体协作者
。它与每个具体协作者都有关联,以便可以向其发送消息。此外,当发生感兴趣的事件时,每个具体协作者必须能够向中介者发送消息。中介者提供了协调逻辑,避免了具体协作者之间相互协调。
通过引入中介者,我们可以将原本分散在具体协作者之间的交互和依赖关系集中到中介者身上,从而降低了系统的复杂性。中介者负责管理和维护具体协作者之间的关系,确保它们按照预定的规则和顺序进行交互。这样,具体协作者之间不再需要直接相互引用和调用,而是通过中介者来进行间接的交互,提高了系统的灵活性和可维护性。
同时,中介者模式也使得我们可以更容易地添加、删除或替换具体协作者,而不需要对整个系统进行大规模的修改。因为中介者封装了与具体协作者的交互逻辑,所以当我们需要改变某个具体协作者的行为时,只需要修改中介者与该协作者的交互逻辑即可,而不需要修改其他具体协作者或整个系统的代码。这大大提高了系统的可扩展性和可维护性。
效果
此模式创建了一个中介者,用于协调一组协作执行器,但不需要这些设备之间的直接耦合。通过最小化耦合点并将协调逻辑封装在单个元素内,这大大简化了整体设计。每当协作者需要联系另一个协作者时,它会通知中介者,中介者可以决定如何作为一个整体的协作体进行响应。
由于许多嵌入式系统必须实时控制,因此动作之间的延迟可能会导致不稳定或不良的影响。中介者类能够在这些时间限制内作出反应是非常重要的。当执行器与中介者之间存在双向协作时,这一点尤其令人关注。
实现策略
中介者必须能够与每个具体协作者建立关联。这可以通过指针数组、为每个具体协作者设置单独的指针,或者将两者组合起来实现。一个常见的经验法则是,如果一组具体协作者具有相同的行为(例如,它们遵循一个通用接口),那么指针数组是最佳选择。如果具体协作者有不同的用途,则为每个这样的具体协作者使用单独的链接。
甚至可以将不同的具体协作者分组,其中组内的每个类都提供一个通用接口。这样,中介者可以更容易地管理和协调具有相似功能或特性的协作者集合。
相关模式
此模式与经典《设计模式》书籍中的策略模式相似。在这种情况下,我们关注其在详细硬件协调方面的应用。
策略模式通常用于定义一系列的算法,并将每一个算法封装起来,使它们可以互相替换。策略模式使得算法可以独立于使用它的客户端变化。而在中介者模式中,我们同样看到了一种将行为或策略封装在中介者中的做法,这使得具体协作者之间的交互可以被中介者管理和协调。
在某些情况下,可以用观察者模式代替中介者模式。观察者模式是一种行为设计模式,它定义了对象之间的一对多依赖关系,当一个对象的状态发生改变时,其所有依赖者都会收到通知并自动更新。在中介者模式中,如果具体协作者需要与多个其他对象进行交互,或者我们不希望具体协作者直接知道中介者的存在,那么可以使用观察者模式来实现这种间接的交互。
实例
见原书