GOF设计模式之单例模式

定义

单例模式(Singleton Pattern)的定义如下:Ensure a class only has one instance, and provide a global point of access to it(确保某一个类只有一个实例,并且提供一个全局访问点来访问此实例)。在JVM应用中,单例模式表现为一个类在JVM中只有一个实例。一个相对合理的类图如下:

sg-1

使用场景

  • 1、系统中需要一个共享的访问点或者共享数据,例如Web请求计数器。
  • 2、创建一个对象需要消耗的资源过多,例如IO、数据库资管等。
  • 3、需要定义大量的静态常量或者静态方法(例如工具类),可以考虑采用单例模式。

在JDK中典型的真实例子如下:

  • java.lang.Runtime#getRuntime()
  • java.awt.Desktop#getDesktop()
  • java.lang.System#getSecurityManager()

适用性

单例模式的优势

  • 采用单例模式的类能确保在一个应用中只有一个实例,减少了内存消耗以及创建或者销毁类实例时候的性能损耗。
  • 可以避免对资源的多重占用。
  • 可以设置应用的全局访问点,优化和共享资源访问。

单例模式的劣势

  • 单例模式一般没有接口或者基类,扩展困难,扩展必须修改类代码。
  • 紧密耦合的代码,对测试不利,简单来说就是不能Mock掉。
  • 单例模式违反单一责任原则,因为它既要保持"单例"又要顾及业务逻辑。

实现方式

懒汉方式

懒汉方式的关键字在于"懒",也就是懒加载(Lazy Load),一个很常见的使用方式就是双重检查锁定(Double-Check Locking):

public class Singleton {

    private static volatile Singleton INSTANCE = null;
    
    private Singleton(){

    }

    public static Singleton getInstance(){
        if (null == INSTANCE){
            synchronized (Singleton.class){
                if (null == INSTANCE){
                    INSTANCE = new Singleton();
                }
            }
        }
        return INSTANCE;
    }
}

饿汉方式

饿汉方式的实现相对简单:

public class Singleton {

    private static volatile Singleton INSTANCE = new Singleton();

    private Singleton(){

    }

    public static Singleton getInstance(){
        return INSTANCE;
    }
}

静态内部类方式

使用静态内部类方式的好处是既可以实现延迟加载,又可以保证线程安全,它的实现如下:

public class Singleton {

    private Singleton(){

    }
    
    private static class InterClassHolder{
        private final static Singleton INSTANCE = new Singleton();
    }

    public static Singleton getInstance(){
        return InterClassHolder.INSTANCE;
    }
}

最佳实践-单元素枚举方式

《Effective Java》第2版中指出:单元素枚举类型是实现单例的最佳方式。这是因为,前面说到的三种实现方式都可以通过反射改变类的行为,但是枚举类型可以避免这个问题。建议在所有需要使用到单例模式的情况下直接使用单元素枚举方式实现单例:

public enum Singleton {

    INSTANCE;

    public void sayHello() {

    }
}

使用方式:Singleton.INSTANCE.sayHallo()

故事

Doge是公司里一个核心项目的开发组长,手下有十多个组员分别负责开发项目的不同模块。

sg-2
sg-3

Doge展示了一个日期工具类和它的使用情况:

public class DateUtils {

    public static String format(LocalDateTime target,String pattern){
        return DateTimeFormatter.ofPattern(pattern).format(target);
    }

    public LocalDateTime parse(String target,String pattern){
        return LocalDateTime.parse(target, DateTimeFormatter.ofPattern(pattern));
    }
}

//调用情况
DateUtils.format(LocalDateTime.now(), "yyyy-MM-dd HH:mm:ss");
LocalDateTime target = new DateUtils().parse("2018-7-29 12:12:30","yyyy-MM-dd HH:mm:ss");
sg-3

小黑贴了一下重写的工具类:

public enum DateUtils {

    SINGLETON;

    private static final Map<String, DateTimeFormatter> FORMATTER_CACHE = new HashMap<>();

    public String format(LocalDateTime target, String pattern) {
        return getOrCreateFormatter(pattern).format(target);
    }

    public LocalDateTime parse(String target, String pattern) {
        return LocalDateTime.parse(target, getOrCreateFormatter(pattern));
    }


    private DateTimeFormatter getOrCreateFormatter(String pattern) {
        DateTimeFormatter formatter;
        if (FORMATTER_CACHE.containsKey(pattern)) {
            formatter = FORMATTER_CACHE.get(pattern);
        } else {
            formatter = DateTimeFormatter.ofPattern(pattern);
            FORMATTER_CACHE.put(pattern, formatter);
        }
        return formatter;
    }
}

//调用
DateUtils.SINGLETON.format(LocalDateTime.now(), "yyyy-MM-dd HH:mm:ss");
LocalDateTime target = DateUtils.SINGLETON.parse("2018-7-29 12:12:30", "yyyy-MM-dd HH:mm:ss");
sg-3

Doge拷贝了小黑的工具类代码,并且仿照这个类的逻辑完成了其他工具类的代码重构。

(本文完)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

  • 单例模式(SingletonPattern)一般被认为是最简单、最易理解的设计模式,也因为它的简洁易懂,是项目中最...
    成热了阅读 9,873评论 4 34
  • 前言 本文主要参考 那些年,我们一起写过的“单例模式”。 何为单例模式? 顾名思义,单例模式就是保证一个类仅有一个...
    tandeneck阅读 7,251评论 1 8
  • 1 场景问题# 1.1 读取配置文件的内容## 考虑这样一个应用,读取配置文件的内容。 很多应用项目,都有与应用相...
    七寸知架构阅读 11,787评论 12 68
  • 参考资料:菜鸟教程之设计模式 设计模式概述 设计模式(Design pattern)代表了最佳的实践,通常被有经验...
    Steven1997阅读 4,943评论 1 12
  • 夫未战而庙算胜者,得算多也;未战而庙算不胜者,得算少也。多算胜,少算不胜,而旷于无算乎?吾以此关之,胜负见矣。 带...
    深笑567阅读 1,445评论 0 0

友情链接更多精彩内容