用消息传递代替直接依赖

有时一个模块只需要通知其他人它发生了一些事件/变化,而这些信息以后发生什么并不重要。

在这种情况下,模块之间根本不需要“相互了解”,即包含直接链接并直接交互,但只需交换消息(messages)或事件(events)就足够了。

有时似乎通过消息传递的模块通信比直接依赖要弱得多。事实上,因为没有调用方法,所以没有关于类的信息。但这只不过是一种错觉。

逻辑开始与消息类型、它们的参数和传输的数据相关联,而不是方法名称。这些模块的连接性被抹黑了。

它曾经是这样的:我们调用方法 - 有连接,我们不调用方法 - 没有连接。现在假设模块 A 开始在其消息中发送略有不同的数据。同时,依赖于这些消息的所有模块都将无法正常工作。

假设之前,在添加新用户时,授权模块发送消息USER_ADDED,更新后,尝试注册时开始发送此消息,并在参数中额外指示注册成功与否。

因此,非常胜任地实现消息机制是非常重要的。为此有各种模板。

观察员。它用于一对多依赖的情况,当许多模块依赖于其中一个的状态时——主模块。它使用邮件机制,这意味着主模块简单地向它的所有订阅者发送相同的消息,并且对此信息感兴趣的模块实现“订阅者”接口并订阅邮件列表。

这种方法广泛用于具有用户界面的系统中,允许应用程序(模型)的核心保持独立,同时通知其关联界面某些内容已更改并需要更新。

这里的消息格式在操作系统级别是标准化的,其开发人员必须注意向后兼容性和良好的文档。

通过消息分发来组织交互有一个额外的“好处”——“订阅者”对“发布”(即发送)消息的可选存在。像这样设计良好的系统允许随时添加/删除模块。

消息总线

您可以组织消息交换并为此以不同的方式使用调解器模式。

当模块之间存在多对多依赖关系时使用它。中介者作为模块间通信的中介,充当通信中心,消除了模块之间显式引用的需要。

因此,模块之间的交互(“all with all”)被模块之间仅与中介的交互(“one with all”)所取代。据说中介者封装了多个模块之间的交互。

消息总线

这就是所谓的智能中介。正是在那里,开发人员最常开始添加他们的拐杖,这通过打开/关闭接收某些消息来影响各个模块的行为。

一个典型的现实例子是机场交通管制。来自飞机的所有消息都会发送到控制器的控制塔,而不是直接在飞机之间发送。控制器已经决定哪些飞机可以起飞或降落,然后向飞机发送消息。

重要的!模块不仅可以相互发送简单的消息,还可以发送命令对象。这种交互由命令模板描述。底线是将执行特定操作的请求封装为单独的对象。

事实上,这个对象包含一个单独的execute()方法,然后它允许您将此操作作为参数传递给其他模块以执行,并且通常可以使用可以对普通对象执行的命令对象执行任何操作。

得墨忒耳法则

Demeter 法则禁止使用隐式依赖:“如果对象 A 可以访问对象 B,对象 B 可以访问对象 C,则对象 A 不能直接访问对象 C。”

这意味着代码中的所有依赖项都必须是“明确的”——类/模块只能在其工作中使用“它们的依赖项”,而不应通过它们爬升到其他依赖项。一个很好的例子是三层架构。接口层应该与逻辑层一起工作,但不应该直接与数据库层交互。

简而言之,这条原则也可以这样表述:“只与最直接的朋友互动,不要与朋友的朋友互动。” 这会降低代码的连贯性,同时提高其设计的可见性和透明度。

Demeter 法则实现了已经提到的“最少知识原则”,这是松散耦合的基础,包括一个对象/模块应该知道尽可能少的关于其他对象/模块的结构和属性的细节,以及一般的任何东西,包括它自己的组件

生活中的一个比喻:如果你想让狗跑,指挥它的爪子是愚蠢的,不如把命令交给狗,它会自己处理自己的爪子。

组合而不是继承

这是一个非常大且有趣的话题,至少值得单独讲一讲。在达成共识之前,互联网上关于这个话题的很多副本都被打破了——我们使用继承到最低限度,组合——到最大。

关键是继承实际上提供了类之间最强的联系,所以应该避免。Herb Sutter 的文章“ Prefer Composition Over Inheritance ”中详细介绍了这个主题。

当您开始学习设计模式时,您会遇到一大堆控制对象创建或其内部结构的模式。顺便说一句,在这种情况下,我建议您注意来自游戏的委托/委托模式和组件模式。

我们稍后会详细讨论模式。