You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 17:25:03