Kotlin带参单例模式的优雅实现

4,093 阅读2分钟

kotlin经典单例实现

我们都知道 Kotlin 为我们实现单例提供了很方便的实现,一个关键词就可以搞定:那就是 object

object SomeSingleton

反编译成 Java 代码:

public final class SomeSingleton {
   public static final SomeSingleton INSTANCE;

   private SomeSingleton() {
      INSTANCE = (SomeSingleton)this;
   }

   static {
      new SomeSingleton();
   }
}

可以看出,是通过静态内部类实现的。它是《java并发编程实践》推荐的实现单例的方式。因为这种方式不仅能够保证单例对象的唯一性,同时也延迟了单例的实例化。

关于 java 的几种单例设计模式实现方法,可以参考笔者之前写的一篇博客:

设计模式之单例模式

带参优雅实现

自动化在带来快捷便利的同时,就意味着失去一定的灵活性。

object 方式的实现带来的一个局限就是不能自由传参。因为 Kotlinobject 关键字不允许存在任何构造函数。

或许你会想到可以通过注入的方式去实现,但是这样还是太麻烦,如果忘记去调用这个方法就会出问题,相信他人也不太喜欢这样的方式去获取你写的单例对象。

有没有更为优雅的方式实现呢?

当然是有的。

我们需要参考 Kotlin 标准库中的 lazy() 函数的实现思路。

把创建和初始化带有参数的单例的逻辑封装起来。并通过双重检查锁定算法实现逻辑的线程安全。

open class SingletonHolder<out T, in A>(creator: (A) -> T) {

    private var creator: ((A) -> T)? = creator
    @Volatile
    private var instance: T? = null

    fun getInstance(arg: A): T {
        val i = instance
        if (i != null) {
            return i
        }

        return synchronized(this) {
            val i2 = instance
            if (i2 != null) {
                i2
            } else {
                val created = creator!!(arg)
                instance = created
                creator = null
                created
            }
        }
    }
    
    //对上述方法的一种更简洁的写法
    fun getInstance2(arg: A): T =
        instance ?: synchronized(this) {
            instance ?: creator!!(arg).apply { 
                instance = this 
            }
        }

}

这个类一撸完,它就像一个爸爸的存在。有了它接下来我们实现单例就变得异常简单,举个栗子

我想实现一个带 context 参数的 SkinManager 单例

class SkinManager private constructor(context: Context) {
    companion object : SingletonHolder<SkinManager, Context>(::SkinManager)
}

使用方式:

SkinManager.getInstance(context)

好了,游戏结束,就这么简单~