设计模式(五)The Singleton Pattern 单例模式

来源:互联网 发布:两个excel筛选重复数据 编辑:程序博客网 时间:2024/05/29 08:51

问题引入

        有时候对于有些对象,我们只需要一个,多了反而会出现很多问题。比如:线程池,缓存,处理器偏好设置,日志对象等等(可能导致程序异常,内存泄露)。

模式定义

        确保一个类只有一个实例,并提供一个全局访问点。

认识模式

        确实我们可以通过程序员之间的约定保证一个类只有一个对象,但是通过使用单例模式效果更好,更安全。

问题解决

       创建单例模式类步骤:   

         * 1、构造一个私有的静态变量;

         * 2、构造函数私有化,避免外部直接创建对象;

         * 3、创建一个公有的静态方法,返回该变量,如果该变量为空,创建该对象。

直接上代码:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package my.oschina.net.design.singleton;
 
class SingleTon
{
    private static SingleTon instance = null;
     
    private SingleTon(){}
 
    public static SingleTon getInsatnce()
    {  
        if(null == instance)
            instance = new SingleTon();         
        return instance;
    }      
}

这是一个经典的单例模式代码,但是它本身是有问题的,在多线程的情况下,可能会出现多个对象。于是我们做如下的改进

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
class SingleTon
{
    private static SingleTon instance = null;
     
    private SingleTon(){}
 
    public static synchronized SingleTon getInsatnce()
    {  
        if(null == instance)
            instance = new SingleTon(); 
         
        return instance;
    }      
}

我们给这个方法加上同步机制,这样就不存在多线程的问题了,但是新的问题来了,实际上只有在第一次执行这个方法的时候才需要同步,以后实际上就不需要同步。但是这样写代码,我们每调用一次都需要同步,这显然是很费时费事的。于是我们再进行改进。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class SingleTon {
    private static SingleTon instance = null;
     
    private SingleTon() {}
     
    public static SingleTon getInstance() {
        if (null == instance ) {
            synchronized (SingleTon.class) {
                if (null == instance) {
                    SingleTon = new Siglenton();
                }
            }
        }
        return instance ;
    }
}

这个就好多了,我们称这个为双重检查(DOUBLE-CHECKED),我们先检查对象是否已经创建,如果没有创建我们在进行同步,这样一来只有第一次会同步,刚好符合我们的要求。


上面所说的我们都称他为“懒汉模式”,我们延迟了对象的实例化,下面我们看看所谓的"饿汉模式”

?
1
2
3
4
5
6
7
8
9
class Singleton {  

//Go 

ahead 

and 

create 

an 

instance 

of 

Singleton 

in 

a 

static 

initializer. 

 

This 

code 

is 

guaranteed 

to 

be 

thread 

safe! 

//Using this approach, we rely on the JVM to create the unique instance of the Singleton when the class is loaded. 

//The JVM guarantees that the instance will be created before any thread accesses the static uniqueInstance variable. 

    private static Singleton instance= new Singleton();  
    //可以保证线程安全
    private Singleton() {}  
     
    public static Singleton getInstance() {  
        return instance;  
    }  
}


还有一种使用静态内部类的方法

?
1
2
3
4
5
6
7
8
9
10
11
public class Singleton {  
    private static class SingletonHolder {  
        private static final Singleton instance= new Singleton();  
    }  
     
    private Singleton() {} 
      
    public static final Singleton getInstance() {  
        return SingletonHolder.instance;  
    }  
}



单例模式,可以说是GOF的23种设计模式中最简单的一个。
  这个模式相对于其他几个模式比较独立,它只负责控制自己的实例化数量单一(而不是考虑为用户产生什么样的实例),很有意思,是一个感觉上很干净的模式,本人很喜欢这个模式。
  android中很多地方都用到了单例模式,本文以输入法管理者InputMethodManager为例,展开分析。
  单例模式,Singleton Pattern,能够以其特有的优势,替代系统中全局变量,应用非常广泛。

  1.意图
  保证一个类仅有一个实例,并提供一个访问它的全局访问点。
  热门词汇:单例 唯一 私有构造

  2.结构

  android中有很多系统级别的全局变量,如时间,输入法,账户,状态栏等等,android中对这些都直接或者有些间接用到了单例模式。

  以输入法为例,把上图修改为实际情况:

  非常的简单,但是有一点,从上面我们也看到了synchronized关键字,在多线程的环境下,单例模式为了保证自己实例数量的唯一,必然会做并发控制。
  类似这种线程安全的单例,跨进程的单例,参数化的单例等等的情况,确实超出本文的范围,而且都涉及到很多东西,是一个很大的话题,不好展开。

  3. 代码

  public final class InputMethodManager {

      static final Object mInstanceSync = new Object();//同步

      //内部全局唯一实例

      static InputMethodManager mInstance; 

      //对外api

      static public InputMethodManager getInstance(Context context) {

          return getInstance(context.getMainLooper());

      }     

      /**

       * 内部api,供上面的外部api调用

       * @hide 系统隐藏的api

       */

      static public InputMethodManager getInstance(Looper mainLooper) {

          synchronized (mInstanceSync) {

              if (mInstance != null) {

                  return mInstance;

              }

              IBinder b = ServiceManager.getService(Context.INPUT_METHOD_SERVICE);

              IInputMethodManager service = IInputMethodManager.Stub.asInterface(b);

              mInstance = new InputMethodManager(service, mainLooper);

          }

          return mInstance;

      }

  }

  客户端调用,比如contextimpl中的getSystemService()方法中如下调用:

  class ContextImpl extends Context{

      @Override

      public Object getSystemService(String name) {

          if (WINDOW_SERVICE.equals(name)) {

              //... ... 省略下面n个if,else if

          } else if (INPUT_METHOD_SERVICE.equals(name)) {

              //获取输入法管理者唯一实例

              return InputMethodManager.getInstance(this);

          }  else if (KEYGUARD_SERVICE.equals(name)) {

               //... ... 省略下面n个if,else if

          } else if (ACCESSIBILITY_SERVICE.equals(name)) {

              //又见单例,无处不在

              return AccessibilityManager.getInstance(this);

          } else if (LOCATION_SERVICE.equals(name)) {

              //... ... 省略下面n个if,else if

          }  else if (NFC_SERVICE.equals(name)) {

              return getNfcManager();

          }

          return null;

      }

  }

  非常简单,干净的一个模式。

  4.效果

  创建型模式。
  对唯一实例的受控访问。
  避免全局变量污染命名空间。
  允许对操作和表示的精化。
  比类操作更灵活。


0 0
原创粉丝点击