通俗易懂设计模式(单例模式)

86 阅读4分钟

单例模式(Singleton Pattern)是一种创建型设计模式,它确保一个类只有一个实例,并提供全局访问点。单例模式的核心思想是将类的实例化过程限制在类内部,并通过一个静态方法返回唯一的实例。

单例模式的优点:

  1. 控制实例数目:可以精确地控制一个类的实例数目,避免重复创建资源消耗过多。
  2. 节约资源:由于系统中只存在一个实例,可以减少系统开销,例如只需要创建一次数据库连接。
  3. 提高性能:避免不必要的系统资源的消耗,使系统性能得到提升。
  4. 全局访问:单例模式会将实例存储在全局范围内,因此可以通过全局访问点访问实例,方便地实现各种功能。

单例模式的缺点:

  1. 不适用于有多个相似对象的场景:单例模式只适用于全局只需要一个实例的场景,如果需要多个实例,单例模式将不适用。
  2. 不支持子类继承:由于单例模式将构造函数设置为私有,导致无法通过子类继承来扩展类的功能。
  3. 单例模式可能导致代码耦合度增加:由于单例模式将实例的创建和使用融合在一起,可能导致代码的耦合度增加,降低代码的可维护性。

Java 实现单例模式的示例代码:

public class Singleton {
    // 使用静态变量保存唯一实例
    private static Singleton instance;

    // 将构造方法设为私有,防止外部实例化
    private Singleton() {
    }

    // 提供全局访问点,获取唯一实例
    public static synchronized Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

在这个示例中,我们将构造方法设为私有,防止外部实例化。同时,我们使用静态变量 instance 保存唯一实例,并通过静态方法 getInstance() 返回实例。为了保证线程安全,我们在 getInstance() 方法上添加了 synchronized 关键字。

使用单例模式时,需要注意以下几点:

  1. 单例模式应用场景较为独特,需要根据实际需求谨慎使用。
  2. 单例模式需要考虑线程安全问题,可以使用双重检查锁定(Double-Checked Locking)或静态内部类(Static Inner Class)等方法实现线程安全的单例模式。
  3. 单例模式可能导致代码耦合度增加,需要谨慎使用,并在需求变化时及时调整设计。

场景举例

场景一:数据库连接池

在应用程序中,频繁地建立和关闭数据库连接会导致性能下降。为了解决这个问题,我们可以使用数据库连接池来复用已经建立的数据库连接。数据库连接池需要一个全局唯一的实例来管理所有的数据库连接。

使用单例模式来实现数据库连接池,可以确保整个应用程序中只有一个数据库连接池实例,从而避免了多个连接池实例导致的资源浪费和性能问题。具体实现如下:

public class DatabaseConnectionPool {
    private static DatabaseConnectionPool instance;
    private Map<String, Connection> connections;

    private DatabaseConnectionPool() {
        connections = new HashMap<>();
    }

    public static synchronized DatabaseConnectionPool getInstance() {
        if (instance == null) {
            instance = new DatabaseConnectionPool();
        }
        return instance;
    }

    public Connection getConnection(String url, String username, String password) {
        String key = url + username + password;
        Connection connection = connections.get(key);
        if (connection == null) {
            try {
                connection = DriverManager.getConnection(url, username, password);
                connections.put(key, connection);
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        return connection;
    }

    public void releaseConnection(Connection connection) {
        // 将连接放回连接池的逻辑
    }
}

场景二:日志记录器

在应用程序中,日志记录是一项非常重要的功能。为了方便地在程序的各个部分记录日志,我们可以使用一个全局的日志记录器实例。这个实例需要是全局唯一的,以便在整个应用程序中共享日志记录的功能。

使用单例模式来实现日志记录器,可以确保整个应用程序中只有一个日志记录器实例,从而避免了多个日志记录器实例导致的资源浪费和性能问题。具体实现如下:

public class Logger {
    private static Logger instance;
    private PrintWriter writer;

    private Logger() {
        try {
            writer = new PrintWriter("log.txt", "UTF-8");
        } catch (FileNotFoundException | UnsupportedEncodingException e) {
            e.printStackTrace();
        }
    }

    public static synchronized Logger getInstance() {
        if (instance == null) {
            instance = new Logger();
        }
        return instance;
    }

    public void log(String message) {
        writer.println(new Date() + ": " + message);
        writer.flush();
    }
}

在这两个场景中,我们都使用了单例模式来管理全局唯一的实例。在第一个场景中,我们使用单例模式来实现数据库连接池,确保了整个应用程序中只有一个数据库连接池实例;在第二个场景中,我们使用单例模式来实现日志记录器,确保了整个应用程序中只有一个日志记录器实例。通过使用单例模式,我们可以避免资源的浪费和性能问题,同时提高了代码的可维护性和可扩展性。