理解设计模式:使用英雄示例的单例!(里面有蝙蝠侠和蜘蛛侠)。

2025-06-04

理解设计模式:使用英雄示例的单例!(里面有蝙蝠侠和蜘蛛侠)。

经典的设计模式有23种,详见原著《设计模式:可复用面向对象软件的元素》。这些模式为软件开发中经常遇到的特定问题提供了解决方案。

在本文中,我将描述单例模式;以及如何以及何时应用它。

单例模式:基本思想

在软件工程中,单例模式是一种软件设计模式,它将一个类的实例化限制为一个“单一”实例。当只需要一个对象来协调整个系统的操作时,这种方法非常有用。该术语源于数学概念“单例”。——维基百科

确保一个类只有一个实例,并提供一个全局访问点。——设计模式:可复用面向对象软件的元素

此模式的主要特点是每个类中只实例化一个对象。此外,还会为该类创建一个入口点,通常使用访问器方法,例如getInstance

该模式的 UML 图如下:

Singleton 类是一个单独的类,它有一个自己的属性,名为uniqueInstance,用于存储 Singleton 类的实例。该类的构造函数是私有的,只能通过访问器方法(例如 )访问该实例getInstance

访问器方法负责在单个实例存在的情况下返回该实例,或者在尚未实例化的情况下实例化该实例。

在以下情况下应使用单例模式:

  1. 必须有一个类的单个实例,并且客户端必须能够从他们已知的访问点访问该类。

  2. 单例类可以通过继承进行扩展,并且客户端必须能够使用扩展类而无需对其进行任何更改。

单例模式有几个优点,总结如下几点:

  • 严格控制客户端访问单例实例的方式和时间。由于单例类封装了其实例,因此访问是受控的。

  • 当您需要限制我们从一个类创建的实例的数量以节省系统资源时。

  • 单例模式是对全局变量的改进,因为它避免了使用仅存储单例实例的全局变量来污染名称空间。

  • 由于单例简化了代码,因此代码更易于使用、理解和测试。

现在我将向您展示如何使用 JavaScript/TypeScript 实现此模式。在我们的例子中,我设计了一个问题,其中有一个名为 的类,DatabaseConnection它定义了两个属性:配置 和getUniqueIdentificator。这个类是与数据库的连接。被多个客户端(和)DatabaseConnection使用。下面的 UML 图展示了我刚才描述的场景。client1client2

客户端代码关联如下:

每个客户端都会创建一个新的数据库连接,并请求每个连接的唯一标识符。这种架构的主要后果之一是使用了比实际需要更多的资源。

该类DatabaseConnection如下:

在前面的类中可以看到,只有私有属性可用于数据库的配置,而唯一标识符则使用公共属性来访问。

最后,本次交互的示例代码如下:

得到的结果如下图所示:

如您所见,每个数据库实例都有一个唯一的标识符,因为它们是不同的实例,但它们执行的任务完全相同。实际上,最智能的做法是使用单个实例来建立连接。

解决方案是使用单例模式,该模式只会创建类的一个实例。例如,使用单例模式的新 UML 图如下所示:

与之相关的代码DatabaseConnection如下:

访问该实例的唯一方法是使用 getDatabaseConnection 静态方法,如果该实例不存在或需要获取该实例,该方法将创建一个新实例。这样,客户端只需稍加修改即可使用此实例,而无需创建自己的实例:

这些修改之后程序执行的结果如下图所示:

我创建了两个 npm 脚本,在应用 Singleton 模式后运行此处显示的两个示例。

npm run example1-problem
npm run example1-singleton-solution1

另一个使用单例模式解决的有趣例子是,当有多个类必须是单例时。例如,蜘蛛侠蝙蝠侠这两个英雄组合就是单例。在下面的UML图中,你可以看到这种情况:

与客户端相关的代码如下:

接下来,我们将创建独一无二的英雄。首先,我们将定义一个通用接口,用于存放每个英雄所包含的信息:

我们的英雄们各有特色,但也有一些共同的属性和方法,为此,我们定义了一个名为 的父类,HeroBase它包含了蜘蛛侠和蝙蝠侠的共同特征。这个类如下:

蝙蝠侠和蜘蛛侠都在其构造中实现了单例模式,并存储了对每个类(我们的英雄!)的唯一对象的引用。这些类如下:

最后,本次交互的示例代码如下:

得到的结果如下图所示:

我创建了一个 npm 脚本,在应用 Singleton 模式后运行此处显示的示例。

npm run example2-singleton-solution1

单例模式可以避免项目的复杂性,因为您可以在客户端熟知的单点上严格控制类的实例化。此外,它还可以节省系统资源,因为它不会实例化一组执行相同任务的类,而是使用该类的单个实例。然而,这种模式名声不佳,甚至被认为是反模式,因为它实际上创建了全局变量,这些变量可以在代码中的任何位置访问和更改。

最重要的不是像我展示的那样去实现这个模式,而是能够识别这个特定模式能够解决的问题,以及何时应该或不应该实现该模式。这一点至关重要,因为具体实现会根据你使用的编程语言而有所不同。

结论

单例模式可以避免项目的复杂性,因为您可以在客户端熟知的单点上严格控制类的实例化。此外,它还可以节省系统资源,因为它不会实例化一组执行相同任务的类,而是使用该类的单个实例。然而,这种模式名声不佳,甚至被认为是反模式,因为它实际上创建了全局变量,这些变量可以在代码中的任何位置访问和更改。

最重要的不是像我展示的那样去实现这个模式,而是能够识别这个特定模式能够解决的问题,以及何时应该或不应该实现该模式。这一点至关重要,因为具体实现会根据你使用的编程语言而有所不同。

更多更多更多...

最初于 2019 年 5 月 2 日发布于https://www.carloscaballero.io

文章来源:https://dev.to/carlillo/understanding-design-patterns-singleton-using-hero-examples-batman-and-spiderman-are-inside-2pm5
PREV
了解 ITCSS:GhostCMS 博客中使用 ITCSS 的真实案例
NEXT
理解设计模式:使用 Long Night (GOT) 示例的装饰器!