Kotlin单例模式:对象 vs 私有构造函数的类

5

谷歌的向日葵示例应用程序使用一个私有类和其伴生对象来实现其仓库的单例模式,而不是将仓库实现为一个(本质上是单例的)对象。

这是我第一次看到在Kotlin中以这种方式实现单例,而不是将其作为对象实现。在什么情况下应该使用此私有构造函数实现,而不是更常见的对象实现?

class GardenPlantingRepository private constructor(
  private val gardenPlantingDao: GardenPlantingDao
) {
  suspend fun createGardenPlanting(plantId: String) {
    withContext(IO) {
      val gardenPlanting = GardenPlanting(plantId)
      gardenPlantingDao.insertGardenPlanting(gardenPlanting)
    }
  }

  suspend fun removeGardenPlanting(gardenPlanting: GardenPlanting) {
    withContext(IO) {
      gardenPlantingDao.deleteGardenPlanting(gardenPlanting)
    }
  }

  fun getGardenPlantingForPlant(plantId: String) =

    gardenPlantingDao.getGardenPlantingForPlant(plantId)

  fun getGardenPlantings() = gardenPlantingDao.getGardenPlantings()

  fun getPlantAndGardenPlantings() = gardenPlantingDao.getPlantAndGardenPlantings()

  companion object {
    // For Singleton instantiation
    @Volatile private var instance: GardenPlantingRepository? = null

    fun getInstance(gardenPlantingDao: GardenPlantingDao) =
      instance ?: synchronized(this) {
        instance ?: GardenPlantingRepository(gardenPlantingDao).also { instance = it }
      }
  }
}
1个回答

3
如果您的单例实例需要参数,例如在此处使用GardenPlantingDao时,使用对象会存在问题,因为它们不能接受构造函数参数。在Android上经常出现这种情况,因为许多情况下单例需要Context才能操作。
在这些情况下,您仍然可以使用对象,但这要么不安全,要么不方便:
第一种选择是在使用任何其他方法之前使用setter方法提供其依赖项。这意味着每个其他方法都必须检查依赖项是否已初始化,并且可能在它们没有被初始化的情况下抛出异常,从而导致运行时问题。
或者,您可以要求将任何依赖项作为单例每个方法的参数,但这样做在调用站点上很繁琐。
因此,“传统”的实现单例的方式是使用私有构造函数和工厂方法。

1
我不知道为什么Kotlin不直接扩展语言功能,提供带参数的对象,这样你就不需要查看100篇帖子和解决方案来学习正确的做法了。 - David

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接