设计模式(一):简介及单例模式

前言

无论是在日常的工作中还是面试中,都必须会涉及到设计模式方面的知识,日常开发中,单例模式,代理模式等会经常使用。面试的时候也会被要求手写一些常用的设计模式。这里在此做个汇总:
《》

作用

  • 提高代码维护性,可拓展性
  • 提高代码复用率,降低开发成本
  • 提高代码可读行,让代码容易让他人理解

设计原则

设计模式遵循以下原则:

  • 1.单一职责原则
    一个类只有一种情况能引起它的变化,通俗的讲,一个类不要什么乱七八糟的东西都有,它的作用要简单纯粹。
  • 2.开闭原则
    一个类一旦被引用,就尽量不要去修改原来的东西,可以通过添加拓展来实现想要的效果,更好的热插拔。
  • 3.里氏替换原则
    是对开闭原则的补充,基类可以实现的行为,它的子类也可以实现。当子类替换掉基类时,既能复用父类,也能在子类上拓展新的行为。
  • 4.依赖倒转原则
    是开闭原则的基础,日常开发中,要面向接口编程,面向抽象而不是面向具体。在使用具体的实现类时,尽量不与其他的具体实现类交互,而是与具体实现类的上层接口交互。这个在Java开发中十分常见。
  • 5.接口隔离原则
    如果一个接口,实现类出现了没有具体实现该接口的方法,那么要把接口拆分出来写,反过来讲,即拆分出来的接口中的方法在实现类中要全部实现。
  • 6.合成复用原则
    要尽量使用合成/聚合,尽量不要使用继承。

单例模式

一个简单的单例有以下3点要素

  1. 声明当前类的静态私有变量
  2. 构造方法私有化
  3. 公有方法返回唯一实例
  • 简单懒汉式单例:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
     public class Singleton {
    //1. 声明私有变量 ourInstance(用以记录 Singleton 的唯一实例)
    private static Singleton ourInstance = null;

    //2. 把类的构造方法私有化,不让外部调用构造方法实例化
    private Singleton() {
    }
    //3. 定义公有方法提供该类的全局唯一访问点
    //4. 外部通过调用getInstance()方法来返回唯一的实例
    public static Singleton newInstance() {
    if( ourInstance == null){//懒汉式
    ourInstance = new Singleton();
    }
    return ourInstance;
    }
    }

这样即可保证在进程中只有一个实例。
通常情况下单线程用上面的方法是没有问题的,但如果把它放进多线程的环境下,就会出现问题:多线程并发同时调用newInstance()方法,会重复创建实例。对此,需要进行加锁优化。

  • 添加同步锁懒汉式单例:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    class Singleton{ 

    private static Singleton instance = null;

    private Singleton(){}

    public static Singleton getInstance(){
    // 加入同步锁
    synchronized(Singleton.class) {
    if (instance == null)
    instance = new Singleton();
    }
    return instance;
    }
    }

添加了同步锁之后可以避免多线程并发引起的安全问题。
但是,同时加锁之后,每次访问都要进行线程同步,会耗时耗能。
综合上述问题,可采用静态内部类单例。

  • 静态内部类懒汉式单例:
    在调用newInstance()方法时必须要先实例了Singleton,不能在newInstance中实例Singleton。利用静态内部类来完成调用前的加载。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    class Singleton {

    // 1. 创建静态内部类
    private static class Singleton2 {
    // 在静态内部类里创建单例
    private static Singleton ourInstance = new Singleton();
    }

    // 私有构造函数
    private Singleton() {
    }

    // 延迟加载、按需创建
    public static Singleton newInstance() {
    return Singleton2.ourInstance;
    }

    }
  1. 外部调用类的newInstance()
  2. 自动调用Singleton2.ourInstance
  3. 此时单例类Singleton2得到初始化
  4. 而该类在装载 & 被初始化时,会初始化它的静态域,从而创建单例;
  5. 由于是静态域,因此只会JVM只会加载1遍,Java虚拟机保证了线程安全性
  6. 最终只创建1个单例

-饿汉式单例
唯一的区别是:
饿汉式:单例创建时机不可控,即类加载时,自动创建单例
懒汉式:单例创建时机可控,即有需要时,才手动创建单例
饿汉时单例是线程安全,能避免懒汉式因多线程引发的各种问题。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Singleton {

// 1. 加载该类时,单例就会自动被创建
private static Singleton ourInstance = new Singleton();

// 2. 构造函数 设置为 私有权限
// 原因:禁止他人创建实例
private Singleton() {
}

// 3. 通过调用静态方法获得创建的单例
public static Singleton newInstance() {
return ourInstance;
}
}

总结

主要介绍了设计模式的基本概念和单例模式,其他设计模式可参看:
设计模式(二):简单工厂与策略模式