理解设计模式:使用英雄示例的单例!(里面有蝙蝠侠和蜘蛛侠)。
经典的设计模式有23种,详见原著《设计模式:可复用面向对象软件的元素》。这些模式为软件开发中经常遇到的特定问题提供了解决方案。
在本文中,我将描述单例模式;以及如何以及何时应用它。
单例模式:基本思想
在软件工程中,单例模式是一种软件设计模式,它将一个类的实例化限制为一个“单一”实例。当只需要一个对象来协调整个系统的操作时,这种方法非常有用。该术语源于数学概念“单例”。——维基百科
确保一个类只有一个实例,并提供一个全局访问点。——设计模式:可复用面向对象软件的元素
此模式的主要特点是每个类中只实例化一个对象。此外,还会为该类创建一个入口点,通常使用访问器方法,例如getInstance
。
该模式的 UML 图如下:
Singleton 类是一个单独的类,它有一个自己的属性,名为uniqueInstance
,用于存储 Singleton 类的实例。该类的构造函数是私有的,只能通过访问器方法(例如 )访问该实例getInstance
。
访问器方法负责在单个实例存在的情况下返回该实例,或者在尚未实例化的情况下实例化该实例。
在以下情况下应使用单例模式:
-
必须有一个类的单个实例,并且客户端必须能够从他们已知的访问点访问该类。
-
单例类可以通过继承进行扩展,并且客户端必须能够使用扩展类而无需对其进行任何更改。
单例模式有几个优点,总结如下几点:
-
严格控制客户端访问单例实例的方式和时间。由于单例类封装了其实例,因此访问是受控的。
-
当您需要限制我们从一个类创建的实例的数量以节省系统资源时。
-
单例模式是对全局变量的改进,因为它避免了使用仅存储单例实例的全局变量来污染名称空间。
-
由于单例简化了代码,因此代码更易于使用、理解和测试。
现在我将向您展示如何使用 JavaScript/TypeScript 实现此模式。在我们的例子中,我设计了一个问题,其中有一个名为 的类,DatabaseConnection
它定义了两个属性:配置 和getUniqueIdentificator
。这个类是与数据库的连接。被多个客户端(和)DatabaseConnection
使用。下面的 UML 图展示了我刚才描述的场景。client1
client2
客户端代码关联如下:
每个客户端都会创建一个新的数据库连接,并请求每个连接的唯一标识符。这种架构的主要后果之一是使用了比实际需要更多的资源。
该类DatabaseConnection
如下:
在前面的类中可以看到,只有私有属性可用于数据库的配置,而唯一标识符则使用公共属性来访问。
最后,本次交互的示例代码如下:
得到的结果如下图所示:
如您所见,每个数据库实例都有一个唯一的标识符,因为它们是不同的实例,但它们执行的任务完全相同。实际上,最智能的做法是使用单个实例来建立连接。
解决方案是使用单例模式,该模式只会创建类的一个实例。例如,使用单例模式的新 UML 图如下所示:
与之相关的代码DatabaseConnection
如下:
访问该实例的唯一方法是使用 getDatabaseConnection 静态方法,如果该实例不存在或需要获取该实例,该方法将创建一个新实例。这样,客户端只需稍加修改即可使用此实例,而无需创建自己的实例:
这些修改之后程序执行的结果如下图所示:
我创建了两个 npm 脚本,在应用 Singleton 模式后运行此处显示的两个示例。
npm run example1-problem
npm run example1-singleton-solution1
另一个使用单例模式解决的有趣例子是,当有多个类必须是单例时。例如,蜘蛛侠和蝙蝠侠这两个英雄组合就是单例。在下面的UML图中,你可以看到这种情况:
与客户端相关的代码如下:
接下来,我们将创建独一无二的英雄。首先,我们将定义一个通用接口,用于存放每个英雄所包含的信息:
我们的英雄们各有特色,但也有一些共同的属性和方法,为此,我们定义了一个名为 的父类,HeroBase
它包含了蜘蛛侠和蝙蝠侠的共同特征。这个类如下:
蝙蝠侠和蜘蛛侠都在其构造中实现了单例模式,并存储了对每个类(我们的英雄!)的唯一对象的引用。这些类如下:
最后,本次交互的示例代码如下:
得到的结果如下图所示:
我创建了一个 npm 脚本,在应用 Singleton 模式后运行此处显示的示例。
npm run example2-singleton-solution1
单例模式可以避免项目的复杂性,因为您可以在客户端熟知的单点上严格控制类的实例化。此外,它还可以节省系统资源,因为它不会实例化一组执行相同任务的类,而是使用该类的单个实例。然而,这种模式名声不佳,甚至被认为是反模式,因为它实际上创建了全局变量,这些变量可以在代码中的任何位置访问和更改。
最重要的不是像我展示的那样去实现这个模式,而是能够识别这个特定模式能够解决的问题,以及何时应该或不应该实现该模式。这一点至关重要,因为具体实现会根据你使用的编程语言而有所不同。
结论
单例模式可以避免项目的复杂性,因为您可以在客户端熟知的单点上严格控制类的实例化。此外,它还可以节省系统资源,因为它不会实例化一组执行相同任务的类,而是使用该类的单个实例。然而,这种模式名声不佳,甚至被认为是反模式,因为它实际上创建了全局变量,这些变量可以在代码中的任何位置访问和更改。
最重要的不是像我展示的那样去实现这个模式,而是能够识别这个特定模式能够解决的问题,以及何时应该或不应该实现该模式。这一点至关重要,因为具体实现会根据你使用的编程语言而有所不同。
更多更多更多...
-
设计模式:可重用面向对象软件的元素,作者:Gamma、Helm、Johnson 和 Vlissides,Addison Wesley,1995 年。
-
https://www.dofactory.com/javascript/singleton-design-pattern
-
https://github.com/sohamkamani/javascript-design-patterns-for-humans#-singleton
-
这篇文章的GitHub分支是https://github.com/Caballerog/blog/tree/master/singleton-pattern
最初于 2019 年 5 月 2 日发布于https://www.carloscaballero.io。
文章来源:https://dev.to/carlillo/understanding-design-patterns-singleton-using-hero-examples-batman-and-spiderman-are-inside-2pm5