23种设计模式漫画版系列—单例模式

简介: 23种设计模式漫画版系列—单例模式

01意图


单例模式是一种创建型设计模式 让你能够保证一个类只有一个实例 并提供一个访问该实例的全局节点


02问题


单例模式同时解决了两个问题 所以违反了_单一职责原则_

    1.  保证一个类只有一个实例 为什么会有人想要控制一个类所拥有的实例数量 最常见的原因是控制某些共享资源 例如数据库或文件 的访问权限

它的运作方式是这样的 如果你创建了一个对象 同时过一会儿后你决定再创建一个新对象 此时你会获得之前已创建的对象 而不是一个新对象

注意 普通构造函数无法实现上述行为 因为构造函数的设计决定了它必须总是返回一个新对象

客户端甚至可能没有意识到它们一直都在使用同一个对象

  2. 为该实例提供一个全局访问节点 还记得你 好吧 其实是我自己 用过的那些存储重要对象的全局变量吗 它们在使用上十分方便 但同时也非常不安全 因为任何代码都有可能覆盖掉那些变量的内容 从而引发程序崩溃

和全局变量一样 单例模式也允许在程序的任何地方访问特定对象 但是它可以保护该实例不被其他代码覆盖

还有一点 你不会希望解决同一个问题的代码分散在程序各处的 因此更好的方式是将其放在同一个类中 特别是当其他代码已经依赖这个类时更应该如此

如今 单例模式已经变得非常流行 以至于人们会将只解决上文描述中任意一个问题的东西称为单例


03解决方案


所有单例的实现都包含以下两个相同的步骤

  • 将默认构造函数设为私有 防止其他对象使用单例类的 new运算符
  • 新建一个静态构建方法作为构造函数 该函数会 偷偷 调用私有构造函数来创建对象 并将其保存在一个静态成员变量中 此后所有对于该函数的调用都将返回这一缓存对象

如果你的代码能够访问单例类 那它就能调用单例类的静态方法 无论何时调用该方法 它总是会返回相同的对象


04真实世界类比


政府是单例模式的一个很好的示例 一个国家只有一个官方政府 不管组成政府的每个人的身份是什么  某政府 这一称谓总是鉴别那些掌权者的全局访问节点


05单例模式结构


06伪代码


在本例中 数据库连接类即是一个单例 该类不提供公有构造函数 因此获取该对象的唯一方式是调用 获取实例方法 该方法将缓存首次生成的对象 并为所有后续调用返回该对象

// 数据库类会对`getInstance(获取实例)`方法进行定义以让客户端在程序各处
// 都能访问相同的数据库连接实例。
class Database is
    // 保存单例实例的成员变量必须被声明为静态类型。
    private static field instance: Database
    // 单例的构造函数必须永远是私有类型,以防止使用`new`运算符直接调用构
    // 造方法。
    private constructor Database() is
        // 部分初始化代码(例如到数据库服务器的实际连接)。
        // ...
    // 用于控制对单例实例的访问权限的静态方法。
    public static method getInstance() is
        if (Database.instance == null) then
            acquireThreadLock() and then
                // 确保在该线程等待解锁时,其他线程没有初始化该实例。
                if (Database.instance == null) then
                    Database.instance = new Database()
        return Database.instance
    // 最后,任何单例都必须定义一些可在其实例上执行的业务逻辑。
    public method query(sql) is
        // 比如应用的所有数据库查询请求都需要通过该方法进行。因此,你可以
        // 在这里添加限流或缓冲逻辑。
        // ...
class Application is
    method main() is
        Database foo = Database.getInstance()
        foo.query("SELECT ...")
        // ...
        Database bar = Database.getInstance()
        bar.query("SELECT ...")
        // 变量 `bar` 和 `foo` 中将包含同一个对象。

07应用场景

  • 如果程序中的某个类对于所有客户端只有一个可用的实例 可以使用单例模式
  • 单例模式禁止通过除特殊构建方法以外的任何方式来创建自身类的对象 该方法可以创建一个新对象 但如果该对象已经被创建 则返回已有的对象
  • 如果你需要更加严格地控制全局变量 可以使用单例模式
  • 单例模式与全局变量不同 它保证类只存在一个实例 除了单例类自己以外 无法通过任何方式替换缓存的实例
  • 请注意 你可以随时调整限制并设定生成单例实例的数量 只需修改 获取实例方法 即 getInstance 中的代码即可实现

08单例模式优缺点


  • 你可以保证一个类只有一个实例
  • 你获得了一个指向该实例的全局访问节点
  • 仅在首次请求单例对象时对其进行初始化
  • 违反了_单一职责原则 该模式同时解决了两个问题
  • 单例模式可能掩盖不良设计 比如程序各组件之间相互了解过多等
  • 该模式在多线程环境下需要进行特殊处理 避免多个线程多次创建单例对象
  • 单例的客户端代码单元测试可能会比较困难 因为许多测试框架以基于继承的方式创建模拟对象 由于单例类的构造函数是私有的 而且绝大部分语言无法重写静态方法 所以你需要想出仔细考虑模拟单例的方法 要么干脆不编写测试代码 或者不使用单例模式
    09Python、Java 代码示例

详见次条文章


09

10、推荐UML使用工具


亿图图示

关注公众号:全栈芬达,回复亿图图示,获取激活版。

初学者秒会的专业级UML图绘制软件。无需掌握复杂操作,可以零基础轻松绘制280+种绘图类型


Visio


11、UML  类图关系

UML类图非常简单,可以用下面的图表示一个类:


该图表示一个叫做Person的类,该类有name、age、sex三个private属性,每个属性的类型紧跟在冒号的后面。该类有walk和speak两个方法,其中walk方法是public的,而speak方法是protected的,两个方法的返回值类型紧跟在冒号的后面。

+:公有属性,其它类可以访问该属性

-:私有属性,不能被其它类访问(默认为私有)

\#:保护属性,只能被本类及其派生类访问

~:包内可见,可以被本包中的其它类访问

如果要表示一个接口,则用下面的图表示:

下面介绍类与类之间的关系。如果按照关系的紧密程度从弱到强划分,类与类之间的关系包括:

  • 依赖
  • 关联
  • 聚合
  • 组合
  • 实现
  • 继承


依赖关系

依赖关系是所有类间关系中最弱的一种,它用下面的图表示:


图中的箭头方向表示依赖的方向,上图表示类A依赖类B。

依赖,顾名思义表示一个实体的存在必须依赖另一个实体的存在。可以这样认为,如果类A依赖类B,那么类A只有在类B存在的情况下,才能编译通过。

下面代码是依赖的一个例子:

public class UserController {
private UserService userService;
public User query(Strint userId) {
User user = userService.queryUser(userId);
return user;
}
}

在这段代码,UserController类同时依赖于UserService和User两个类,可以用下面的类图表示它们的依赖关系:


可见依赖关系大量的存在于我们的代码中,但千万不要在项目设计时将全部的依赖关系都画出来,这不仅很累,而且也没有必要。当梳理依赖关系时,先要搞清楚你关注什么,想表达什么,只画出真正需要画的就可以。


关联关系

关联关系表示两个实体间存在一定的联系,这种联系比依赖关系更紧密,不仅仅只是“两个实体触碰到”这样松散的关系。例如Student和School这两个类,一个学生一定会有一个对应的学校,那么Student和School间就存在关联关系,且它们的关系是一对多的。


用下面的UML图表示:



关联关系也可以用于领域建模,例如要设计一个骰子游戏,游戏者连续投掷两次筛子,如果两次点数的总数是7,则游戏者赢,否则游戏者输。可以用下面UML图对这个问题进行领域建模,各实体间使用的就是关联关系。这也是关联关系的一种特殊用法。

聚合&组合

聚合也是一种关联关系,但是这种关联关系存在整体与部分的语义。例如大雁和大雁群,一只大雁是整个大雁群的一部分。这就是一种聚合关系,具有has-a的语义。下面的UML图用来描述聚合关系。

组合是一种强聚合关系,它表示整体和部分之间具有相同的生命周期,同生共死。例如鸟和翅膀,鸟如果死掉了,那么它的翅膀也会跟着死掉。组合关系具有contains-a的语义。下面的UML图用于表达组合关系。


记忆聚合和组合UML图画法的小技巧:菱形就相当于一个容器,容器指向的实体就是整体,所以上面图中的菱形分别指向大雁群和鸟。此外,由于组合关系的紧密程度比聚合关系更强,所以组合关系用实心菱形,聚合关系用空心菱形。


继承&实现

继承和实现都是Java中的基础,比较容易理解,它们是类与类之间关系最强的。分别用下面的UML图表示。

继承示例:



实现示例:


PS:实现关系应该用空心箭头

相关文章
|
29天前
|
设计模式 安全 Java
Kotlin教程笔记(57) - 改良设计模式 - 单例模式
Kotlin教程笔记(57) - 改良设计模式 - 单例模式
27 2
|
10天前
|
设计模式 安全 Java
Kotlin教程笔记(57) - 改良设计模式 - 单例模式
Kotlin教程笔记(57) - 改良设计模式 - 单例模式
|
18天前
|
设计模式 存储 数据库连接
PHP中的设计模式:单例模式的深入理解与应用
【10月更文挑战第22天】 在软件开发中,设计模式是解决特定问题的通用解决方案。本文将通过通俗易懂的语言和实例,深入探讨PHP中单例模式的概念、实现方法及其在实际开发中的应用,帮助读者更好地理解和运用这一重要的设计模式。
14 1
|
1月前
|
设计模式 存储 数据库连接
PHP中的设计模式:单例模式的深入解析与实践
在PHP开发中,设计模式是提高代码可维护性、扩展性和复用性的关键技术之一。本文将通过探讨单例模式,一种最常用的设计模式,来揭示其在PHP中的应用及优势。单例模式确保一个类仅有一个实例,并提供一个全局访问点。通过实际案例,我们将展示如何在PHP项目中有效实现单例模式,以及如何利用这一模式优化资源配置和管理。无论是PHP初学者还是经验丰富的开发者,都能从本文中获得有价值的见解和技巧,进而提升自己的编程实践。
|
1月前
|
设计模式 安全 Java
C# 一分钟浅谈:设计模式之单例模式
【10月更文挑战第9天】单例模式是软件开发中最常用的设计模式之一,旨在确保一个类只有一个实例,并提供一个全局访问点。本文介绍了单例模式的基本概念、实现方式(包括饿汉式、懒汉式和使用 `Lazy<T>` 的方法)、常见问题(如多线程和序列化问题)及其解决方案,并通过代码示例详细说明了这些内容。希望本文能帮助你在实际开发中更好地应用单例模式,提高代码质量和可维护性。
29 1
|
1月前
|
设计模式 缓存 数据库连接
探索PHP中的设计模式:单例模式的实现与应用
在PHP开发中,设计模式是提高代码可复用性、可维护性和扩展性的重要工具。本文将深入探讨单例模式(Singleton Pattern)的基本概念、在PHP中的实现方式以及实际应用场景。单例模式确保一个类仅有一个实例,并提供全局访问点。通过具体代码示例和详细解释,我们将展示如何在PHP项目中有效利用单例模式来解决实际问题,提升开发效率和应用性能。
|
1月前
|
设计模式 存储 测试技术
PHP中的设计模式:单例模式的深入解析与实践
在PHP开发领域,设计模式是解决常见问题的最佳实践。本文将深入探讨单例模式,一种确保类只有一个实例的设计模式,并提供实际应用示例。我们将从单例模式的基本概念讲起,通过实际案例展示如何在PHP中实现单例模式,以及它在不同场景下的应用和优势。最后,我们会探讨单例模式的优缺点,帮助开发者在实际项目中做出明智的选择。
|
1月前
|
设计模式 SQL 安全
PHP中的设计模式:单例模式的深入探索与实践在PHP开发领域,设计模式是解决常见问题的高效方案集合。它们不是具体的代码,而是一种编码和设计经验的总结。单例模式作为设计模式中的一种,确保了一个类仅有一个实例,并提供一个全局访问点。本文将深入探讨单例模式的基本概念、实现方式及其在PHP中的应用。
单例模式在PHP中的应用广泛,尤其在处理数据库连接、日志记录等场景时,能显著提高资源利用率和执行效率。本文从单例模式的定义出发,详细解释了其在PHP中的不同实现方法,并探讨了使用单例模式的优势与注意事项。通过对示例代码的分析,读者将能够理解如何在PHP项目中有效应用单例模式。
|
1月前
|
设计模式 存储 数据库连接
PHP中的设计模式:单例模式的深入探讨与实践
在PHP开发领域,设计模式是提升代码可读性、可维护性和扩展性的重要工具。本文聚焦于单例模式——一种确保类只有一个实例,并提供全局访问点的设计模式。我们将从定义、实现方式、应用场景以及在PHP框架中的运用等方面进行详细探讨,旨在帮助PHP开发者更好地理解和运用单例模式。
|
24天前
|
设计模式 安全 Java
Kotlin教程笔记(57) - 改良设计模式 - 单例模式
Kotlin教程笔记(57) - 改良设计模式 - 单例模式
24 0