一个看似简单的问题:什么是设计模式?

一则或许对你有用的小广告

欢迎加入小哈的星球 ,你将获得:专属的项目实战 / Java 学习路线 / 一对一提问 / 学习打卡/ 赠书活动

目前,正在 星球 内带小伙伴们做第一个项目:全栈前后端分离博客项目,采用技术栈 Spring Boot + Mybatis Plus + Vue 3.x + Vite 4手把手,前端 + 后端全栈开发,从 0 到 1 讲解每个功能点开发步骤,1v1 答疑,陪伴式直到项目上线,目前已更新了 204 小节,累计 32w+ 字,讲解图:1416 张,还在持续爆肝中,后续还会上新更多项目,目标是将 Java 领域典型的项目都整上,如秒杀系统、在线商城、IM 即时通讯、权限管理等等,已有 870+ 小伙伴加入,欢迎点击围观

在软件行业,几乎每家公司都会讨论一系列话题。我相信设计模式的故事就是其中之一。您可以找到无数支持或反对他们的帖子、文章和 Quora/Stackoverflow 答案。例如,前几天,我遇到了这个古老的 Quora 问题

“为什么现在程序员很少谈论设计模式?哪些模式(如果有的话)仍然有价值?”

作者指的是面向对象的设计模式,也就是《四人帮》一书中介绍的 23 种模式,大多数回答问题的人都认为是这样的。然而,“设计模式”是一些人用来代替“面向对象设计模式”的术语——所以当您看到“设计模式已死”这样奇怪的说法时,不要感到震惊。

在本文中,我们将回答一个看似简单的问题:什么是设计模式?回答这个问题可以帮助我们在讨论或应用特定设计模式时确定我们在谈论什么。

蝙蝠侠和小丑:“什么是设计模式?”

设计模式对您意味着什么?

让我们把所有的陈词滥调放在一边,关注一下在思考设计模式概念时你想到的是什么。这是一个不应该被打破的规则吗?是罗嗦的学术推荐吗?这是另一个设计约束吗?还是您只需要知道就可以通过求职面试?

对我来说,这就像我脑海中的耳语声,阻止我重新发明轮子。您可能有过在遇到问题后立即开始设计/构建过程并创建自己的解决方案的冲动——如果是这样,欢迎加入俱乐部。但在这种情况下,如果解决方案已经存在,创建另一个解决方案只会占用不必要的时间和精力。虽然直接跳入创建自己的解决方案的冲动可能会随着时间的推移而平息,但我相信帮助我们克服这种冲动的关键工具之一是设计模式概念。

设计模式让我想起许多软件工程师之前已经遇到过同样的问题,并且出于多种原因以特定的方式解决了它。它背后可能有一个基本原理,它使我们能够从不同的角度看待问题,即使我们不打算遵循该模式。

我们来看一段GoF名著的介绍:

“我们都知道设计经验的价值。有多少次你有设计似曾相识的感觉——那种感觉你以前解决过一个问题但不知道确切的位置或方法?如果你能记住上一个问题的细节以及你是如何解决它的,那么你就可以重用这些经验而不是重新发现它。”

或多或少,我们都经历过类似的事情:多次解决一个常见的设计问题,却记不起之前的解决方案。通过重用解决方案来解决反复出现的问题,设计模式节省了时间和精力,而这两者都可以用于其他地方。

现在让我们深入了解一些基本概念和定义。

模式是什么意思?

什么是模式?模式概念不是我们行业特有的东西。其他学科和行业如建筑学、经济学等也应用了这个概念。让我们看看建筑界的权威克里斯托弗·亚历山大对模式的定义:

“每个模式都是一个由三部分组成的规则,它表达了特定上下文、问题和解决方案之间的关系。” ( 亚历山大,1979 年

“每个模式都描述了一个在我们的环境中反复出现的问题,然后描述了该问题解决方案的核心,这样您就可以使用该解决方案一百万次,而无需以相同的方式进行操作两次。” ( 亚历山大,1977 年

现在让我们看看模式在软件行业中意味着什么:

“软件架构模式描述了在特定设计环境中出现的特定反复出现的设计问题,并为其解决方案提供了经过充分验证的通用方案。解决方案方案是通过描述其组成部分、它们的职责和关系以及它们协作的方式来指定的。” ( BMRSS, 96 )

基于以上定义,我们可以将模式视为上下文、问题和解决方案的三元组以及它们之间的关系。然而,它是一个广泛的概念,可以应用于不同的规模和抽象级别,从设计整个软件系统到解决属于特定编程语言的设计问题。

不同的模式类别

模式根据其规模和抽象级别进行分类:

  • 架构模式
  • 设计模式
  • 成语

图案类别

架构模式专注于整个软件系统结构、其子系统、不同的广泛组件以及它们如何组合在一起。这些模式处于较高的抽象层次。例如,他们不关心编程语言是什么或正在使用什么框架。

“架构模式表达了软件系统的基本结构组织模式。它提供了一组预定义的子系统,指定了它们的职责,并包括用于组织它们之间关系的规则和指南。” (英国医学科学研究院,96)

另一方面,我们有一些处于最低抽象级别的模式,称为习惯用法,它们同时处理设计和实现。习语是语言特定的,即语言 A 中的习语与语言 B 无关。

“习语是特定于编程语言的低级模式。习语描述了如何使用给定语言的特性来实现组件的特定方面或它们之间的关系。” (英国医学科学研究院,96)

但是,在上述关于规模和抽象级别的模式之间仍然存在一些其他模式,这就是设计模式的用武之地。

深入了解设计模式

那么我们再回顾一下剩余类的定义:

“设计模式提供了一种方案,用于改进软件系统的子系统或组件,或它们之间的关系。它描述了一种常见的通信组件结构,可以解决特定上下文中的一般设计问题。” (英国医学科学研究院,96)

设计模式是中等规模的,这意味着它们既不像架构模式那样高度抽象,也不像习语那样与编程语言耦合。应用设计模式不会影响软件系统结构,但会影响子系统的体系结构及其较小的组件。

现在,让我们讨论以下陈述(摘自同一参考文献),其中包含关于设计模式的一个重要事实:

“它们往往独立于特定的编程语言或编程范式。” (英国医学科学研究院,96)

根据我们之前在本文中提到的模式类别,我们最好这样改写:

“它们独立于特定的编程语言,但通常(不总是)依赖于编程范式。”

声明的第一部分很清楚:如果模式依赖于特定的编程语言,那么它将属于习语类别。关于第二部分,设计模式是试图解决编程范式中出现的常见问题。它们可能是对范式缺陷的回应。例如,GoF 书中描述了 23 种面向对象的设计模式,其中大部分并不是函数式编程范式中的真正关注点。但反之亦然,因为设计模式具有不同的上下文。

正如您在下图中看到的,不同的范例可以共享设计模式,但我们不打算讨论这个主题,因为它超出了本文的范围。

不同范式可以共享设计模式

分析一个著名的陈述

考虑到前面的部分,让我们快速浏览一下您在简单地使用 google 设计模式时可能会遇到的语句:

函数式语言不需要设计模式。

您可以在此处找到样本。这种说法是错误的,因为它暗示设计模式只属于面向对象范式。然而,设计模式是一个可以同时出现在函数式编程范式和面向对象范式中的概念。设计模式可能只是特定于宿主范例的问题和需求。例如,函数式编程范式具有特定于其上下文的设计模式

此外,正如我们在前面几节中讨论的那样,不同的范式有不同的背景,因此会遇到不同的问题。我们不应该期望范式 B 的设计模式能够解决范式 A 的问题。

快速回顾

与其他模式类型类似,设计模式试图解决特定上下文中反复出现的问题。

  • 它们与语言无关,但主要是范式耦合。
  • 作为中等规模,它们不像架构模式那样抽象。
  • 它们的应用不影响软件系统的基本架构,但影响子系统的架构。
  • 最后但同样重要的是,它们是(所有类型的模式)防止我们重新发明轮子的手段。

在我看来,设计模式并不是约束我们设计的严格法则。它们既不是固定的也不是完美的,因为它们可以进化并变得更好甚至消失,但模式概念仍然存在。遵循模式就像一种心态,可以帮助我们找出可能有更好的解决方案。