登记式单例实现单例模式的继承(限定一个抽象类的所有子类都必须是单例)

      一直都在想如何在Java写一个抽象类,让该抽象类的所有子类都限定为单例模式,一个类需要设计成单例时直接继承该抽象类,而单例的限定与实例获取交给抽象类来完成。一个传统的单例类形如一下形式:

 1 public class Singleton {
 2     private static final Singleton singleton = new Singleton();
 3     
 4     //限制产生多个对象
 5     private Singleton(){
 6     }
 7     
 8     //通过该方法获得实例对象
 9     public static Singleton getInstance(){
10         return singleton;
11     }
12     
13     //类中的其他方法
14     public void doSomething(){
15     }
16 
17 }

     根据单例实例构造的时机和方式不同,单例模式还可以分成几种,但对于这种通过私有化构造函数,静态方法提供实例的单例类而言,是不支持继承的。这种模式的单例实现要求每个具体的单例类自身来维护单例实例和限制多个实例的生成。可以采用另外一种实现单例的思路:登记式单例,来使得单例对继承开放。

     

 1 import java.lang.reflect.Constructor;
 2 import java.lang.reflect.InvocationTargetException;
 3 import java.util.HashMap;
 4 import java.util.Map;
 5 
 6 public abstract class AbstractSingleton {
 7     private static Map<String, AbstractSingleton> registryMap = new HashMap<String, AbstractSingleton>();
 8     
 9     AbstractSingleton() throws SingletonException{
10         String clazzName = this.getClass().getName();
11         if (registryMap.containsKey(clazzName)){
12             throw new SingletonException("Cannot construct instance for class " + clazzName + ", since an instance already exists!");
13         } else {
14             synchronized(registryMap){
15                 if (registryMap.containsKey(clazzName)){
16                     throw new SingletonException("Cannot construct instance for class " + clazzName + ", since an instance already exists!");
17                 } else {
18                     registryMap.put(clazzName, this);
19                 }
20             }
21         }
22     }
23     
24     @SuppressWarnings("unchecked")
25     public static <T extends AbstractSingleton> T getInstance(final Class<T> clazz) throws InstantiationException, IllegalAccessException{
26         String clazzName = clazz.getName();
27         if(!registryMap.containsKey(clazzName)){
28             synchronized(registryMap){
29                 if(!registryMap.containsKey(clazzName)){
30                     T instance = clazz.newInstance();
32                     return instance;
33                 }
34             }
35         }
36         return (T) registryMap.get(clazzName);
37     }
38     
39     public static AbstractSingleton getInstance(final String clazzName) 
40             throws ClassNotFoundException, InstantiationException, IllegalAccessException{
41         if(!registryMap.containsKey(clazzName)){
42             Class<? extends AbstractSingleton> clazz = Class.forName(clazzName).asSubclass(AbstractSingleton.class);
43             synchronized(registryMap){
44                 if(!registryMap.containsKey(clazzName)){
45                     AbstractSingleton instance = clazz.newInstance();
47                     return instance;
48                 }
49             }
50         }
51         return registryMap.get(clazzName);
52     }
53     
54     @SuppressWarnings("unchecked")
55     public static <T extends AbstractSingleton> T getInstance(final Class<T> clazz, Class<?>[] parameterTypes, Object[] initargs) 
56             throws SecurityException, NoSuchMethodException, IllegalArgumentException, 
57             InvocationTargetException, InstantiationException, IllegalAccessException{
58         String clazzName = clazz.getName();
59         if(!registryMap.containsKey(clazzName)){
60             synchronized(registryMap){
61                 if(!registryMap.containsKey(clazzName)){
62                     Constructor<T> constructor = clazz.getConstructor(parameterTypes);
63                     T instance = constructor.newInstance(initargs);
65                     return instance;
66                 }
67             }
68         }
69         return (T) registryMap.get(clazzName);
70     }
71     
72     static class SingletonException extends Exception {
73         /**
74          * 
75          */
76         private static final long serialVersionUID = -8633183690442262445L;
77 
78         private SingletonException(String message){
79             super(message);
80         }
81     }
82 
83 }

      以上代码实现了一个抽象类,该类使用一个静态的Map来维护各个子类的实例。在构造方法中判断对应子类的实例是否已登记,若已登记则抛出一个SingletonException阻止实例的创建。由于构造子类的实例必须先执行父类的构造方法,因此子类第一次通过构造方法创建对象时,父类构造方法会自动把实例登记,以后再调用该子类的构造方法则会抛出异常,即便子类构造方法是public的,也只能成功创建一个实例。同时父类提供几个getInstance方法,通过传入需要获取实例的子类Class对象或Class限定名来获取对应的实例。前两个方法需要子类提供无参构造方法,第三个getInstance方法提供子类只有有参构造方法的情况下,通过参数构造子类对象。下面给出两个具体实现类的例子:

 1 public class ConcreteSingleton1 extends AbstractSingleton {
 2 
 3     public ConcreteSingleton1() throws SingletonException {
 4         super();
 5         // TODO Auto-generated constructor stub
 6     }
 7     
 8     public static ConcreteSingleton1 getInstance(){
 9         try {
10             return AbstractSingleton.getInstance(ConcreteSingleton1.class);
11         } catch (InstantiationException e) {
12             // TODO Auto-generated catch block
13             // will not happen
14             e.printStackTrace();
15             return null;
16         } catch (IllegalAccessException e) {
17             // TODO Auto-generated catch block
18             // will not happen
19             e.printStackTrace();
20             return null;
21         }
22     }
23 
24 }
25 
26 public class ConcreteSingleton2 extends AbstractSingleton {
27     private final int value;
28 
29     public ConcreteSingleton2(int value) throws SingletonException {
30         super();
31         // TODO Auto-generated constructor stub
32         this.value = value;
33     }
34     
35     public static ConcreteSingleton2 getInstance(int value){
36         try {
37             return AbstractSingleton.getInstance(ConcreteSingleton2.class, new Class<?>[]{int.class}, new Object[]{value});
38         } catch (Exception e) {
39             // TODO Auto-generated catch block
40             e.printStackTrace();
41             return null;
42         } 
43     }
44     
45     public int getValue(){
46         return value;
47     }
48 
49 }

        两个实现类都继承自AbstractSingleton。其中ConcreteSingleton1构造方法无参,ConcreteSingleton2有一个int型参数。两个类都各自提供了一个getInstance方法简化实例的获取。两个类的构造方法都不是私有的,外界可以通过new来视图多次创建对象,但只有该类对象没有创建过任何一个实例时,new才能成功获得对象,否则会抛出异常。两个类中的getInstance方法完全只是为了简化获取单例对象的方式,可以不写直接通过AbstractSingleton中的相应方法获取对象。除此之外,两个类都和正常java类无异,单例的维护和限定彻底交给抽象类来完成,只需要继承AbstractSingleton即可实现单例。

        测试代码如下:

 1 public class TestSingleton {
 2 
 3     /**
 4      * @param args
 5      */
 6     public static void main(String[] args) {
 7         // TODO Auto-generated method stub
 8         ConcreteSingleton1 singleton1 = null;
 9         ConcreteSingleton2 singleton2 = null;
10         
11         // 第一次创建实例,创建成功
12         try {
13             singleton1 = new ConcreteSingleton1();
14             System.out.println(singleton1);
15         } catch (SingletonException e) {
16             // TODO Auto-generated catch block
17             e.printStackTrace();
18         }
19         
20         try {
21             singleton2 = new ConcreteSingleton2(5);
22             System.out.println(singleton2 + "---" + singleton2.getValue());
23         } catch (SingletonException e) {
24             // TODO Auto-generated catch block
25             e.printStackTrace();
26         }
27         
28         
29         // 获取单例实例,与前面new出来的实例是同一个实例
30         singleton1 = ConcreteSingleton1.getInstance();
31         System.out.println(singleton1);
32         
33         singleton2 = ConcreteSingleton2.getInstance(6);
34         System.out.println(singleton2 + "---" + singleton2.getValue());
35         
36         // 试图创建第二个实例,抛出异常,创建失败
37         try {
38             singleton1 = new ConcreteSingleton1();
39             System.out.println(singleton1);
40         } catch (SingletonException e) {
41             // TODO Auto-generated catch block
42             e.printStackTrace();
43         }
44         
45         try {
46             singleton2 = new ConcreteSingleton2(5);
47             System.out.println(singleton2 + "---" + singleton2.getValue());
48         } catch (SingletonException e) {
49             // TODO Auto-generated catch block
50             e.printStackTrace();
51         }
52 
53     }
54 
55 }

运行结果如下:

ConcreteSingleton1@c3c749
ConcreteSingleton2@1bc4459---5
ConcreteSingleton1@c3c749
ConcreteSingleton2@1bc4459---5
AbstractSingleton$SingletonException: Cannot construct instance for class ConcreteSingleton1, since an instance already exists!
    at AbstractSingleton.<init>(AbstractSingleton.java:14)
    at ConcreteSingleton1.<init>(ConcreteSingleton1.java:6)
    at TestSingleton.main(TestSingleton.java:42)
AbstractSingleton$SingletonException: Cannot construct instance for class ConcreteSingleton2, since an instance already exists!
    at AbstractSingleton.<init>(AbstractSingleton.java:14)
    at ConcreteSingleton2.<init>(ConcreteSingleton2.java:7)
    at TestSingleton.main(TestSingleton.java:50)

        虽然采用getInstance方法获取ConcreteSingleton2的对象时传入的参数是6,但是由于之前已经实例化过对象,直接获得之前登记的对象,此时输出的value仍然是5。

        接下来把第一次使用new创建实例的代码注释掉,通过getInstance方法通过反射创建实例,这一次ConcreteSingleton2对象输出的value应该是6,运行结果如下:

ConcreteSingleton1@c3c749
ConcreteSingleton2@1888759---6
AbstractSingleton$SingletonException: Cannot construct instance for class ConcreteSingleton1, since an instance already exists!
    at AbstractSingleton.<init>(AbstractSingleton.java:14)
    at ConcreteSingleton1.<init>(ConcreteSingleton1.java:6)
    at TestSingleton.main(TestSingleton.java:43)
AbstractSingleton$SingletonException: Cannot construct instance for class ConcreteSingleton2, since an instance already exists!
    at AbstractSingleton.<init>(AbstractSingleton.java:14)
    at ConcreteSingleton2.<init>(ConcreteSingleton2.java:7)
    at TestSingleton.main(TestSingleton.java:51)

 

posted on 2014-09-16 20:34  都市耕牛  阅读(5427)  评论(0编辑  收藏  举报