design-patterns - 实现在对象注册表中注册自己的模式
问题描述
我有几个实现抽象类“产品”的类。为了遵循开闭原则,我希望实现类以一种将自己注册在ProductRegistry
单例对象中的方式。添加新Product
类时,编写它们或将它们放在类路径中就足够了,而不必触及ProductRegistry
类。
以下是我的第一步,但似乎这些对象是延迟初始化的。我可以改变它吗?或者我应该在每个子类中使用一个初始化块?(不喜欢这种方法,因为它很容易忘记)。
abstract class Product {
val id : Int
constructor(id : Int) {
println("ctor with id=$id")
this.id = id
ProductRegistry.register(this)
}
abstract fun getColor(): String
}
object BrownProduct : Product(2) {
override fun getColor() = "brown"
}
object ProductRegistry {
private val registry = HashSet<Product>()
fun register(product: Product) {
println("Registering: $product")
registry.add(product)
}
fun fromId(id: Int): Product {
return registry.filter { it.id == id }.first()
}
}
测试:
class TestProdFac {
@Test
fun test1() {
// println(RedProduct) <- if called before, then test works
assertEquals(RedProduct::class, ProductRegistry.fromId(1)::class)
}
}
解决方案
不幸的是,如果没有一些额外的工作,我认为这是不可能的。我能想到的主要可能性是:
使用Java 标准库提供的标准ServiceLoader 。这是官方的做事方式,但需要创建一个清单文件列出实现(此处的文档)或在 Java 9 模块信息中指定实现,我认为 Kotlin 不支持。为避免这种情况,您可以使用Google 的自动服务和kapt,允许您简单地注释您的实现以在编译时生成服务元数据。
您可以手动扫描所有实现的类路径。这还有其他各种缺点:要实现更多的工作,需要对类路径做出假设,并且速度较慢(因为您需要查找并检查每个类)。还有其他问题详细介绍了这种方法,例如这个。
编写您自己的 Kotlin 编译器插件来生成服务元数据,并与第一个选项一起使用。这将需要最多的工作来设置,但使用起来最少。不幸的是,据我所知,关于编译器插件的信息并不多——最好的办法可能是检查官方编译器插件的源代码——比如allopen。
最终,我会推荐使用 ServiceLoader、kapt 和自动服务,如第一点所述。设置起来相对简单——如果你使用 Gradle,你只需要添加 kapt 插件和自动服务依赖项(仅在编译时需要),然后@AutoService
在你的实现中使用注释。它也没有第二种选择的缺点。
推荐阅读
- php - 表单提交中的PHP验证系统
- javascript - 如何在具有打字稿的样式组件中使用 backgroundImage css
- python-3.x - 通过 python3 pip 在 linux 上运行 tensorflow
- c# - C# 覆盖泛型类中的基类属性
- javascript - 在一个 div 中加载数据并清除另一个 div?
- ios - UICollectionViewCompositionalLayout 的背景视图在哪个方法中出列
- sql - PostgreSQL:查询的动态 WHERE 条件
- python - 无法存储 numpy.ndarray' 对象没有属性 'save'
- java - 扫描仪出现问题
- java - Agora 语音(群组)通话 - 如何获取活跃成员/加入回拨?