设计模式-单例模式

来源:互联网 发布:淘宝全屏轮播怎么做 编辑:程序博客网 时间:2024/04/28 05:09

前沿

《设计模式》一书总结了23个模式,依据各自的目的又被分为创建型模式(creational pattern)、结构型模式(structural pattern)和行为型模式(behavioral patterns),它们分别从对象的创建,对象和对象间的结构关系以及对象之间如何交互这三个方面入手,对面向对象系统建模方法给予了解释和指导。

  • 创建型模式描述怎样创建一个对象,以及如何隐藏对象创建的细节,从而使得程序代码不依赖于具体的对象,这样在增加一个新的对象时对代码的改动非常小。
  • 结构型模式描述类和对象之间如何进行有效的组织,形成良好的软件体系结构,主要的方法是使用继承关系来组织各个类。
  • 行为型模式描述类或对象之间如何交互以及如何分配职责,实际上它所牵涉的不仅仅是类或对象的设计模式,还有它们之间的通讯模式。

 

1.      单例模式

回答侧重:概念、类型、使用、注意

 

基本概念

单例对象(Singleton)是一种常用的设计模式,属于创建型模式,在《设计模式》一书中,作者这样来叙述单例模式的:确保一个类只有一个实例并提供一个对它的全局访问指针。在Java应用中,单例对象能保证在一个JVM中,该对象只有一个实例存在。

 

1.1.     单例模式的特点

      单例类只能有一个实例

      单例类必须自己创建自己的唯一实例(在自己类中调用new SingletonClass()

      单例类必须给所有其他对象提供这一实例(提供获取实例的接口)

 

1.2.     实现方法

单例模式的创建有以下几种方式:饿汉式单例,懒汉式单例 和单例注册表。

 

饿汉式单例:

       在这个类被加载时,静态变量instance会被初始化,此时该类的私有构造函数被调用,这时候,单例类的唯一实例就被创建出来了。(注:protected的构造方法可以被其子类以及在同一个包中的其它类调用

public class Singleton1{
       Private
static final
Singleton1 instance=new Singleton1();
      
       //
私有的默认构造函数

       Private Singleton1(){}
      
       //
静态工厂方法

       public
static
Singleton1 getInstance(){
              return instance;
       }
}

 

值得注意的是:由于构造函数是私有的,因此该类不能被继承如果你使用该实现,就无法改变它以便后来你可能想要允许多个单例类的实例Spring会根据配置,描述该类是单例还是原型模式)。

 

懒汉式单例:

         实例并没有直接实例化,而是在静态工厂方法被调用的时候才进行的,而且对静态工厂方法使用了同步化,以处理多线程并发的环境。

Public class Singleton2{
         Private static final Singleton2 instance=null;
        
         //
私有化构造方法来防止外部通过new 来创建该类的实例

         private Singleton1(){}
        
         //
使用 synchronzied 保证线程安全
         Public synchronized static Singleton2 getInstance(){
                  If(instance==null){
                           Instance=new Singleton2();
                  }
                  return instance;
         }
}

缺点:也是不能被继承

 

单例注册表

使用一个单例类注册表可以:

       在运行期指定单例类

       防止产生多个单例类子类的实例

      

       单例注册表模式克服了以上单例类不能被继承的缺点。带注册表单的单例类:

Public class RegSingleton{
         Static private HashMap registry=new HashMap();
         //
静态块,在类被加载时自动执行

         Static{
                  RegSingleton rs=new RegSingleton();
                  registry.put(rs.getClass().getName(),rs);
         }
         //
受保护的默认构造函数,如果为继承关系,则可以调用,克服了单例类不能为继承的缺点

         protected RegSingleton(){}
        
         //
静态工厂方法,返回此类的唯一实例

         public static RegSingleton getInstance(String name){
                  if(name==null){
                            name=” RegSingleton”;
                  }
                  if(registry.get(name)==null){
                           try{
                                    registry.put(name,Class.forName(name).newInstance());
                           }

                           catch(Exception ex){ex.printStackTrace();}
                  }
                  return (RegSingleton)registry.get(name);
         }
}

 

那实际中是怎么应用注册表方式的呢?

首先,封装注册表:

import java.util.HashMap;

import org.apache.log4j.Logger;

 

public class SingletonRegistry {

   public static SingletonRegistry REGISTRY = new SingletonRegistry();

 

   private static HashMap map = new HashMap();

   private static Logger logger = Logger.getRootLogger();

 

   protected SingletonRegistry() {

      // Exists to defeat instantiation

   }

   public static synchronized Object getInstance(String classname) {

      Object singleton = map.get(classname);

 

      if(singleton != null) {

         return singleton;

      }

      try {

         singleton = Class.forName(classname).newInstance();

         logger.info("created singleton: " + singleton);

      }

      catch(ClassNotFoundException cnf) {

         logger.fatal("Couldn't find class " + classname);    

      }

      catch(InstantiationException ie) {

         logger.fatal("Couldn't instantiate an object of type " + 

                       classname);    

      }

      catch(IllegalAccessException ia) {

         logger.fatal("Couldn't access class " + classname);    

      }

      map.put(classname, singleton);

      return singleton;

   }

}

 

注意我是把SingletonRegistry类作为一个单例模式实现的。我也通用化了这个注册表以便它能存储和取回任何类型的对象。例1显示了的Singleton类使用了这个注册表。 


1 使用了一个封装的注册表的Singleton 

import java.util.HashMap;

import org.apache.log4j.Logger;

 

public class Singleton {

 

   protected Singleton() {

      // Exists only to thwart instantiation.

   }

   public static Singleton getInstance() {

      return (Singleton)SingletonRegistry.REGISTRY.getInstance(‘xxx.Singleton’);

   }

}

 

1.3.     Spring的单例实现

当我们试图从Spring容器中取得某个类的实例时,默认情况下,Spring会才用单例模式进行创建。
<bean id="date" class="java.util.Date"/>
<bean id="date" class="java.util.Date" scope="singleton"/> (
仅为Spring2.0支持)
<bean id="date" class="java.util.Date" singleton="true"/>

以上三种创建对象的方式是完全相同的,容器都会向客户返回Date类的单例引用。那么如果我不想使用默认的单例模式,每次请求我都希望获得一个新的对象怎么办呢?很简单,将scope属性值设置为prototype(原型)就可以了

<bean id="date" class="java.util.Date" scope="prototype"/>

通过以上配置信息,Spring就会每次给客户端返回一个新的对象实例。

那么Spring对单例的底层实现,到底是饿汉式单例还是懒汉式单例呢?呵呵,都不是。Spring框架对单例的支持是采用单例注册表的方式进行实现的。不难发现,Spring中众多的单例类都是普通的JAVA BEAN类,并没有任何加工,唯一不同是使用了Spring配置来描述该类的形态(单例、原态),所以只能使用单例注册表来实现其单例模式,源码(部分)如下:

public abstract class AbstractBeanFactory implements ConfigurableBeanFactory{

 

    /**

     * 充当了Bean实例的缓存,实现方式和单例注册表相同

     */

    private final Map singletonCache=new HashMap();

   

    public Object getBean(String name)throws BeansException{

        return getBean(name,null,null);

    }

   

    ...

   

    public Object getBean(String name,Class requiredType,Object[] args)throws BeansException{

        //对传入的Bean name稍做处理,防止传入的Bean name名有非法字符(或则做转码)

        String beanName=transformedBeanName(name);

        Object bean=null;

 

        //手工检测单例注册表

        Object sharedInstance=null;

   

        //使用了代码锁定同步块,原理和同步方法相似,但是这种写法效率更高

        synchronized(this.singletonCache){

            sharedInstance=this.singletonCache.get(beanName);

        }

   

        if(sharedInstance!=null){

            ...

            //返回合适的缓存Bean实例

            bean=getObjectForSharedInstance(name,sharedInstance);

   

        }else{

            ...

            //取得Bean的定义

            RootBeanDefinition mergedBeanDefinition=getMergedBeanDefinition(beanName,false);

            ...

   

            //根据Bean定义判断,此判断依据通常来自于组件配置文件的单例属性开关  

            //<bean id="date" class="java.util.Date" scope="singleton"/>   

            //如果是单例,做如下处理  

            if(mergedBeanDefinition.isSingleton()){    

                synchronized(this.singletonCache){

                    //再次检测单例注册表

                    sharedInstance=this.singletonCache.get(beanName);

                    if(sharedInstance==null){

                        ...

                        try {

                            //真正创建Bean实例

                            sharedInstance=createBean(beanName,mergedBeanDefinition,args);

   

                            //向单例注册表注册Bean实例

                            addSingleton(beanName,sharedInstance);

                        }

                        catch (Exception ex) {...}

                        finally{...}

   

                    }

   

                }

   

                bean=getObjectForSharedInstance(name,sharedInstance);

   

            }

            //如果是非单例,即prototpye,每次都要新创建一个Bean实例

            //<bean id="date" class="java.util.Date" scope="prototype"/>

            else{

                bean=createBean(beanName,mergedBeanDefinition,args);

            }

        }

        ...

        return bean;

    }

}

 

 

转自:

http://xuganggogo.javaeye.com/blog/724511

http://xuganggogo.javaeye.com/blog/724511

 

原创粉丝点击