设计模型|单例模式

Yeren Lv3

定义

单例模式(Singleton Pattern):单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例,这个类称为单例类,它提供全局访问的方法。

单例模式的要点有三个:一是某个类只能有一个实例;二是它必须自行创建这个实例;三是它必须自行向整个系统提供这个实例。单例模式是一种对象创建型模式。单例模式又名单件模式或单态模式。

动机

对于系统中的某些类来说,只有一个实例很重要,例如,一个系统中可以存在多个打印任务,但是只能有一个正在工作的任务;一个系统只能有一个窗口管理器或文件系统;一个系统只能有一个计时工具或ID(序号)生成器。

如何保证一个类只有一个实例并且这个实例易于被访问呢?定义一个全局变量可以确保对象随时都可以被访问,但不能防止我们实例化多个对象。

一个更好的解决办法是让类自身负责保存它的唯一实例。这个类可以保证没有其他实例被创建,并且它可以提供一个访问该实例的方法。这就是单例模式的模式动机。

设计类图

结构

• Singleton:单例

样例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package DesignPattern;

import java.time.LocalDateTime;

public class Singleton {
private static Singleton instance;
private Singleton(LocalDateTime t){
time = t;
};
private LocalDateTime time;

public void SingletonOperation(){
System.out.println(time);
}

public static Singleton getInstance(){
if(instance == null){
instance = new Singleton(LocalDateTime.now());
}
return instance;
}
}
class test{
public static void main(String[] args) {
Singleton a = Singleton.getInstance();
a.SingletonOperation();
Singleton b = Singleton.getInstance();
b.SingletonOperation();
}
}

总结分析

单例模式的目的是保证一个类仅有一个实例,并提供一个访问它的全局访问点。单例模式包含的角色只有一个,就是单例类——Singleton。单例类拥有一个私有构造函数,确保用户无法通过new关键字直接实例化它。除此之外,该模式中包含一个静态私有成员变量与静态公有的工厂方法,该工厂方法负责检验实例的存在性并实例化自己,然后存储在静态成员变量中,以确保只有一个实例被创建。

单例模式适用情况包括:系统只需要一个实例对象;客户调用类的单个实例只允许使用一个公共访问点。

在单例模式的实现过程中,需要注意如下三点:

  • 单例类的构造函数为私有;
  • 提供一个自身的静态私有成员变量;
  • 提供一个公有的静态工厂方法。

优点

  • 提供了对唯一实例的受控访问。因为单例类封装了它的唯一实例,所以它可以严格控制客户怎样以及何时访问它,并为设计及开发团队提供了共享的概念。
  • 由于在系统内存中只存在一个对象,因此可以节约系统资源,对于一些需要频繁创建和销毁的对象,单例模式无疑可以提高系统的性能。
  • 允许可变数目的实例。我们可以基于单例模式进行扩展,使用与单例控制相似的方法来获得指定个数的对象实例。

缺点

  • 由于单例模式中没有抽象层,因此单例类的扩展有很大的困难。
  • 单例类的职责过重,在一定程度上违背了“单一职责原则”。因为单例类既充当了工厂角色,提供了工厂方法,同时又充当了产品角色,包含一些业务方法,将产品的创建和产品的本身的功能融合到一起。
  • 滥用单例将带来一些负面问题,如为了节省资源将数据库连接池对象设计为单例类,可能会导致共享连接池对象的程序过多而出现连接池溢出;现在很多面向对象语言(如Java、C#)的运行环境都提供了自动垃圾回收的技术,因此,如果实例化的对象长时间不被利用,系统会认为它是垃圾,会自动销毁并回收资源,下次利用时又将重新实例化,这将导致对象状态的丢失。

例题

在操作系统中,打印池(Print Spooler)是一个用于管理打印任务的应用程序,通过打印池用户可以添加、删除打印任务或者改变打印任务的优先级,在一个系统中只允许运行一个打印池对象。现使用单例模式来模拟实现打印池的设计。

设计类图

示例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
package DesignPattern;

public class Singleton {
public static void main(String[] args) {
// 获取打印池实例
PrintSpooler spooler1 = PrintSpooler.getInstance();
PrintSpooler spooler2 = PrintSpooler.getInstance();

// 验证单例是否相同
System.out.println("spooler1 == spooler2: " + (spooler1 == spooler2));

// 执行打印池操作
spooler1.addPrintJob("Document1");
spooler2.cancelPrintJob("Document2");
spooler1.changePriority("Document1", 1);
}
}

class PrintSpooler {
// 私有静态实例变量
private static PrintSpooler instance;

// 私有构造函数
private PrintSpooler() {
// 执行初始化操作
}

// 公共静态方法来获取单例实例
public static synchronized PrintSpooler getInstance() {
if (instance == null) {
instance = new PrintSpooler();
}
return instance;
}

// 添加打印任务
public void addPrintJob(String job) {
System.out.println("add print job: " + job);
}
// 删除打印任务
public void cancelPrintJob(String job) {
System.out.println("cancel print job: " + job);
}
// 更改打印任务优先级
public void changePriority(String job, int priority){
System.out.println("change job: " + job + " to NO." + priority);
}
// 其他打印池操作...
}

  • Title: 设计模型|单例模式
  • Author: Yeren
  • Created at : 2023-08-19 00:00:00
  • Updated at : 2023-08-19 00:00:00
  • Link: https://blog.yeren.xyz/2023/08/19/DP-singleton-pattern/
  • License: This work is licensed under CC BY-NC-SA 4.0.
Comments