Kotlin中含20余分支when的HTML生成器工厂该如何优化?
问题描述
我实现了一个工厂类,内部包含多个HTML属性生成器,会根据属性类型返回对应的生成器实例,希望了解是否存在更优的实现方案。
当前工厂类实现如下,when条件分支已经有约20个:
class AttributeHtmlGeneratorFactory { fun create(property: String): AttributeHtmlGenerator { when (property) { "animation" -> { return AnimationHtmlGenerator() } ... "left", "top" -> { return PositionHtmlGenerator() } ... "scaleX" , "scaleY", ... , "direction" -> { return UnusedAttributesHtmlGenerator() } } } }
所有生成器类均实现统一接口:
interface AttributeHtmlGenerator { fun generateHtml(member: KProperty1<HtmlComponentDataModel, *>, component: HtmlComponentDataModel ): String }
生成器调用逻辑:
var result = "" HtmlComponentDataModel::class.memberProperties.forEach { member -> val generator = AttributeHtmlGeneratorFactory().create(member.name) result = result.plus(generator.generateHtml(member, component)) } return result
接口实现示例:
class ButtonFillHtmlGenerator : AttributeHtmlGenerator { override fun generateHtml(member: KProperty1<HtmlComponentDataModel, *>, component: HtmlComponentDataModel): String { var result = "" member.get(component)?.let { result = result.plus("background-color:${it};") } return result } }
优化方案
现有实现的核心问题是硬编码分支违反开闭原则,后续新增属性需要持续修改工厂类,同时无状态对象重复创建也有不必要的性能开销,可以按以下层级优化:
1. 用映射表替换硬编码when分支
把属性和生成器的对应关系从when分支改成注册表结构,工厂本身不需要随生成器新增修改,还能支持外部扩展。
// 无状态的工厂做成单例,避免重复实例化 object AttributeHtmlGeneratorFactory { // 核心注册表:属性名 -> 生成器实例 private val generatorRegistry = mutableMapOf<String, AttributeHtmlGenerator>() // 兜底生成器,处理未匹配的属性 private val defaultGenerator = UnusedAttributesHtmlGenerator() // 初始化时注册所有内置生成器 init { register(AnimationHtmlGenerator(), "animation") register(PositionHtmlGenerator(), "left", "top") // 其余内置生成器在此注册即可 } /** * 注册生成器,支持外部模块新增属性生成规则,无需修改工厂源码 */ fun register(generator: AttributeHtmlGenerator, vararg targetProperties: String) { targetProperties.forEach { propName -> generatorRegistry[propName] = generator } } fun getGenerator(property: String): AttributeHtmlGenerator { return generatorRegistry.getOrDefault(property, defaultGenerator) } }
改完之后新增生成器只需要实现接口、调用register方法绑定对应属性名即可,不用再往工厂的分支里堆代码。
2. 自动注册减少手动维护成本
如果不想每次写完生成器都手动写注册逻辑,可以通过注解+反射扫描的方式自动完成注册:
- 自定义注解标记生成器对应的属性名
@Target(AnnotationTarget.CLASS) @Retention(AnnotationRetention.RUNTIME) annotation class TargetProperties(vararg val propertyNames: String)
- 在生成器类上添加注解
@TargetProperties("animation") class AnimationHtmlGenerator : AttributeHtmlGenerator { // 业务逻辑不变 } @TargetProperties("left", "top") class PositionHtmlGenerator : AttributeHtmlGenerator { // 业务逻辑不变 }
- 工厂初始化时扫描所有标注了
TargetProperties的类,实例化后自动注册到映射表即可,后续新增生成器只需要加注解,连注册代码都不用写。
如果项目接入了Koin、Hilt这类依赖注入框架,可以直接让DI框架托管生成器实例,自动完成注入注册,不需要自己写反射扫描逻辑。
3. 优化调用侧的性能问题
现有调用逻辑每次循环都会新建工厂实例,而你实现的生成器都是无状态的,完全可以复用单例,避免重复创建对象的开销。另外字符串拼接可以换成StringBuilder,减少临时字符串对象的生成:
val resultBuilder = StringBuilder() HtmlComponentDataModel::class.memberProperties.forEach { member -> val generator = AttributeHtmlGeneratorFactory.getGenerator(member.name) val content = generator.generateHtml(member, component) if (content.isNotEmpty()) { resultBuilder.append(content) } } return resultBuilder.toString()
4. 简单场景可以直接用属性注解替代独立生成器
如果大部分属性的生成逻辑都是简单的「属性值转CSS键值对」,不需要复杂的多属性组合逻辑,可以直接在数据类的属性上标记对应的生成规则,遍历属性时直接读取注解生成内容,不用为每个简单属性单独写生成器类:
// 标记属性对应的CSS属性名 @Target(AnnotationTarget.PROPERTY) @Retention(AnnotationRetention.RUNTIME) annotation class CssKey(val key: String) // 标记不需要生成HTML的属性 @Target(AnnotationTarget.PROPERTY) @Retention(AnnotationRetention.RUNTIME) annotation class SkipGenerate data class HtmlComponentDataModel( @CssKey("animation") val animation: String?, @CssKey("left") val left: String?, @CssKey("top") val top: String?, @SkipGenerate val scaleX: Float, @SkipGenerate val scaleY: Float )
只有少数需要复杂生成逻辑的属性,再保留独立的生成器处理即可,能大幅减少重复类的数量。
内容的提问来源于stack exchange,提问作者arianhf
相关产品推荐
相关产品推荐

