Android设计模式解析(二)单例模式(Singleton Pattern)

来源:互联网 发布:windows pe.iso镜像 编辑:程序博客网 时间:2024/05/07 10:53

前言

单例显而易见也就是单独、唯一的意思,即一个类只有一个实例,比较简单、常用的一种设计模式

举个栗子

皇帝:

中国有许多的皇帝,一般同一时间段只会有一个皇帝(个别的两个皇帝的就不说了啊),那我们就能认为皇帝就是一个单例模式,在这个场景中,有皇帝、大臣,大臣每天都需要去上朝参见皇帝,今天参见的皇帝和昨天前天参见的皇帝都是一样的。单例模式,绝对的单例模式

使用场景

  • 该类占用较多的资源,如线程、IO、网络请求等
  • 该类的数据是全局的、共享的
  • 该类的实例生命周期应该是全局的,在application整个生命周期中都会用到的

实现单例模式的几种方法

实现单例模式需要关注几个关键点

  • 构造函数私有化,即权限设置为private
  • 通过一个构造方法或者枚举返回单例类对象
  • 确保单例类的对象只有一个,尤其是多线程的情况下
  • 确保单例对象在反序列表不会重复创建对象

构造函数的私有化,确保了使用者不能够通过new来创建单例类的实例对象,该类暴露给外界一个获取唯一实例的方法,同样还需要保证线程安全,不能够在多线程的时候出现单例类存在多个对象的情况,即单例类在生命周期中只能存在一个实例供外界使用。

实现单例的方式

饿汉式

  private static Singleton singleton= new Singleton();    private Singleton(){    }    private static Singleton getInstance(){        return singleton    }

饿汉式由于类加载时就创建好了对象,不存在线程安全和效率的问题,缺点是过早的创建了对象,而且不能够传递参数。
肯定也没人这么写,假如有推出去斩了

懒汉式

//定义一个实例    private static Singleton singleton = null;    /**     * 构造函数私有化     */    private Singleton() {    }    /**     * 提供一个静态函数,获取实例,synchronized进行同步     * @return     */    public static synchronized Singleton getInstance() {        if (singleton == null) {            singleton = new Singleton();        }        return singleton;    }

这种方式简单,但是每次创建实例对象的时候都需要同步,同步是需要消耗资源的,效率低,不建议使用

Double Check Lock实现单例——改进的懒汉式

目前使用最广泛的单例,线程安全并且效率不低,可以考虑使用

 //定义一个实例    private volatile static Singleton singleton = null;    /**     * 构造函数私有化     */    private Singleton() {    }    /**     * 提供一个静态函数,获取实例,synchronized进行同步     *     * @return     */    public static Singleton getInstance() {        if (singleton == null) {//第一次检查            synchronized (Singleton.class) {//lock                if (singleton == null) {//第二次检查                    singleton = new Singleton();                }            }        }        return singleton;    }

注意到使用了volatile关键字修饰单例对象,这样可以保证singleton对象每次都从主内存中读取,从而避免了由与java内存模型带来不必要的麻烦。
synchronize没有加在方法上,而是加在了方法体上,调用该方法的时候先检查实例对象为空,为空的时候加同步(防止多线程出现多个实例的情况),并且再次判断实例是否为空,为空就创建。这种方式只会在第一次实例为空的情况下同步,克服了懒汉式模式下每次都需要同步的情况,避免了不必要的开支

静态内部类实现单例

比较好的单例,线程安全,保证唯一,延迟实例化。建议选用

 /**     * 构造函数私有化     */    private Singleton() {    }    /**     * 私有内部静态类,利用了加载外部类的时候内部类不会立即被加载的特性     */    private static class SingletonHolder{        private static Singleton singleton = new Singleton();    }    /**     * 提供一个静态函数,获取实例     *     * @return     */    public static Singleton getInstance() {        return SingletonHolder.singleton;    }

使用了java内部类的加载机制,只有内部类的静态成员被调用的时候才会加载静态内部类,所以会延迟加载

枚举单例

public enum  Singleton {    SINGLETON;  private Singleton(){    }}

枚举和java中其他类一样,不仅可以有字段,也可以有自己的方法,如果我们需要一个对象来实现一个简单的事件(显示Toast),就可以使用枚举,枚举默认就是单一实例的并且创建默认是线程安全的

使用容器使用单例

 private static Map<String, Object> instanceMap = new HashMap<String, Object>();    private Singleton() {    }    public static void putInstance(String key, Object instance) {        if (!instanceMap.containsKey(key)) {            instanceMap.put(key, instance);        }    }    public static Object getInstance(String key){        return instanceMap.get(key);    }

这种单例模式在Android源码中有用到,在单例比较多的时候,通过容器来管理和获取单例实例是一个不错的办法,通常在程序初始化的时候会将多个单例实例存到容器中,这样可以管理这些实例,下次使用直接取。

总结

单例模式是面向对象设计模式中应用最简单、最广泛的一种设计模式

  • 单例模式的优点
    1. 提高了系统的性能
    2. 减少了系统内存的开销
    3. 单例模式避免对资源的多重占用保证系统整体的协调
    4. 单例对象可以设置全局的通讯站点, 优化和共享资源访问
  • 单例迷失的缺点
    1. 单例模式一般不存在接口,也不是抽象的,不易拓展,如要拓展需要改变类的源代码,这和开闭原则相违背
    2. 单例模式的对象很容易产生一些问题,如:如为了节省资源将数据库连接池对象设计为的单例类,可能会导致共享连接池对象的程序过多而出现连接池溢出;如果实例化的对象长时间不被利用,系统会认为是垃圾而被回收,这将导致对象状态的丢失。
    3. 单例类的职责过重,某种程度违背了单一职责原则
1 1
原创粉丝点击