单例模式的简化

大家写单例模式的时候有没有这样的困扰:


单例初始化的时候需要一些参数,但是获取单例的时候一般不需要这些参数。传统的单例写法你不得不加上这些参数才可以获取单例对象,这样做是不是很傻呢?


举一个例子,大家常用的数据库操作对象:


public class DbManager {

    private static DbManager sInstance;
    private final Application mApp;

    private DbManager(Application app) {
        mApp = app;
    }

    public static DbManager getInstance(Application app) {
        if (sInstance == null) {
            synchronized (DbManager.class) {
                if (sInstance == null) {
                    sInstance = new DbManager(app);
                }
            }
        }
        return sInstance;
    }
}


一般我们这样来获取单例对象:


DbManager manager = DbManager.getInstance(getApplication());

每次获取单例对象都需要传入 Application,但是真正有用的只有第一次。怎么优化呢? 我们真正想要的 API 是这样的:
DbManager dbManager = InstancePool.get(DbManager.class);

使用来获取类的单例对象,现在我们来思考一下这个 InstancePool 的实现方法。
public static <T> T get(Class<T> clazz)


public static <T> void register(Class<T> clazz, InstanceCreator<T> creator)

InstanceCreator 是一个接口:
public interface InstanceCreator<T> {
    T createInstance();
}




所以整个 InstancePool 是这样的:


public class InstancePool {

    /**
     * 用来保存所有的单例创建回调接口
     */
    private static final Map<Class, InstanceCreator> sCreatorMap = new ConcurrentHashMap<>();

    /**
     * 用于保存所有的单例对象
     */
    private static final Map<Class, Object> sInstanceMap = new ConcurrentHashMap<>();

    /**
     * 先注册某个类是单例类,并指定该类的创建方法
     */
    public static <T> void register(Class<T> clazz, InstanceCreator<T> creator) {
        sCreatorMap.put(clazz, creator);
    }

    /**
     * 根据单例类,获取单例对象
     */
    public static <T> T get(Class<T> clazz) {
        Object instance = sInstanceMap.get(clazz);
        if (instance == null) {
            synchronized (InstancePool.class) {
                if (sInstanceMap.containsKey(clazz)) {
                    instance = sInstanceMap.get(clazz);
                } else {
                    InstanceCreator creator = sCreatorMap.remove(clazz);
                    if (creator != null) {
                        instance = creator.createInstance();
                        sInstanceMap.put(clazz, instance);
                    }
                }
            }
        }
        //noinspection unchecked
        return (T) instance;
    }
}


这样,我们的 DbManager 就可以简化成这样:


public class DbManager {

    private final Application mApp;

    public DbManager(Application app) {
        mApp = app;
    }
}


完全不需要写一大堆 sInstance 相关的代码了。


我已经把代码放到 Github 上了,欢迎大家参考优化。


#Android#