【设计模式】【4】单例设计模式

单例设计模式(Singleton)

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

案例说明

数据库连接

UML图

在这里插入图片描述

  1. 单例(Singleton)类声明了一个名为 getInstance 获取实 例 的静态方法来返回其所属类的一个相同实例。单例的构造函数必须对客户端(Client) 代码隐藏。 调用 获取实例 方法必须是获取单例对象的唯一方式。

核心代码

multithreaded/DatabaseMultiThreaded
/**
 * @author: ccyy
 * @create: 2021-10-13
 * @description: 数据库类会对`getInstance(获取实例)方法
 * 进行定义以让客户端在程序各处 都能访问相同的数据库连接实例。
 **/
public final class DatabaseMultiThreaded {
    /**
     * 保存单例实例的成员变量必须被声明为静态类型。
     */
    private static DatabaseMultiThreaded databaseMultiThreaded;
    public String databaseName;

    /**
     * 单例的构造函数必须永远是私有类型,以防止使用`new`运算符直接调用构造方法。
     * @param databaseName
     */
    private DatabaseMultiThreaded(String databaseName) {
//        确保在该线程等待解锁时,其他线程没有初始化该实例。
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        this.databaseName = databaseName;
    }

    /**
     * 用于控制对单例实例的访问权限的静态方法。
     * @param databaseName
     * @return
     */
    public static DatabaseMultiThreaded getInstance(String databaseName){
        DatabaseMultiThreaded result = databaseMultiThreaded;
        if (result != null){
            return result;
        }
//        控制多线程并发情况
        synchronized (DatabaseMultiThreaded.class){
            if (databaseMultiThreaded == null){
                databaseMultiThreaded = new DatabaseMultiThreaded(databaseName);
            }
            return databaseMultiThreaded;
        }
    }

}

singleThreaded/Database
/**
 * @author: ccyy
 * @create: 2021-10-13
 * @description: 数据库类会对`getInstance(获取实例)方法
 * 进行定义以让客户端在程序各处 都能访问相同的数据库连接实例。
 **/
public final class Database {
    /**
     * 保存单例实例的成员变量必须被声明为静态类型。
     */
    private static Database database;
    public String databaseName;

    /**
     * 单例的构造函数必须永远是私有类型,以防止使用`new`运算符直接调用构造方法。
     * @param databaseName
     */
    private Database(String databaseName) {
//        确保在该线程等待解锁时,其他线程没有初始化该实例。
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        this.databaseName = databaseName;
    }

    /**
     * 用于控制对单例实例的访问权限的静态方法。
     * @param databaseName
     * @return
     */
    public static Database getInstance(String databaseName){
        if (database == null){
            database = new Database(databaseName);
        }
        return database;
    }

}

Main
import com.ccyy.designPattern.creational.singleton.multithreaded.DatabaseMultiThreaded;
import com.ccyy.designPattern.creational.singleton.singleThreaded.Database;

/**
 * @author: ccyy
 * @create: 2021-10-13
 * @description: 测试
 **/
public class Main {
    public static void main(String[] args) {
//        单线程单例模式测试
        Database database1 = Database.getInstance("单线程1");
        Database database2 = Database.getInstance("单线程2");
        System.out.println(database1.databaseName);
        System.out.println(database2.databaseName);
//        多线程测试
        new Thread(() ->{
            DatabaseMultiThreaded databaseMultiThreaded = DatabaseMultiThreaded.getInstance("多线程1");
            System.out.println(databaseMultiThreaded.databaseName);
        }).start();

        new Thread(() ->{
            DatabaseMultiThreaded databaseMultiThreaded = DatabaseMultiThreaded.getInstance("多线程2");
            System.out.println(databaseMultiThreaded.databaseName);
        }).start();
    }
}

适用场景

  • 如果程序中的某个类对于所有客户端只有一个可用的实例, 可以使用单例模式。
  • 单例模式禁止通过除特殊构建方法以外的任何方式来创建自 身类的对象。该方法可以创建一个新对象,但如果该对象已 经被创建,则返回已有的对象。
  • 如果你需要更加严格地控制全局变量,可以使用单例模式。
  • 单例模式与全局变量不同,它保证类只存在一个实例。除了 单例类自己以外,无法通过任何方式替换缓存的实例。

优缺点

优点:

  • 你可以保证一个类只有一个实例。
  • 你获得了一个指向该实例的全局访问节点。
  • 仅在首次请求单例对象时对其进行初始化。

缺点:

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

与其他模式的关系

  • 外观类通常可以转换为单例类,因为在大部分情况下一个外 观对象就足够了。
  • 如果你能将对象的所有共享状态简化为一个享元对象,那么 享元就和单例类似了。但这两个模式有两个根本性的不同。 1. 只会有一个单例实体,但是享元类可以有多个实体,各实 体的内在状态也可以不同。 2. 单例对象可以是可变的。享元对象是不可变的。
  • 抽象工厂、生成器和原型都可以用单例来实现。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值