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

在Kotlin无DI场景下,如何编写基类实现DRY原则?

Refactoring Two User Classes to Follow DRY with an Abstract Base Class

Hey there! Let's fix that duplicate code you're dealing with. Your two User classes are nearly identical—only differing in how they initialize the name property. Here are two clean approaches to extract the common logic into an abstract base class:

Approach 1: Base Class Accepts a Pre-Initialized Name Instance

This is my go-to for cases where the initialization logic is simple and you might want to merge the two User classes into one with multiple constructors:

// Abstract base class holding shared logic
abstract class BaseUser(protected val name: Name) {
    // Shared method - no more repetition!
    fun sayName() {
        println(name.fakeName)
    }
}

// Single User class with two constructors covering both cases
class User : BaseUser(NameGenerator()) {
    // Secondary constructor for the suggestion-based initialization
    constructor(suggestion: String) : this(NameGenerator(suggestion))
}

How this works:

  • The BaseUser takes a fully initialized Name instance in its constructor, so it doesn't need to worry about how Name is created—only that it exists.
  • The User class uses a primary constructor for the no-argument case, and a secondary constructor to handle the suggestion parameter, passing the appropriate NameGenerator result up to the base class.
  • You can now use both User() and User("my-suggestion") just like your original two classes, but with zero duplicated code.

Approach 2: Abstract name Property for Subclass-Specific Initialization

If you prefer to keep the two separate classes (maybe they'll diverge more later), this approach lets each subclass handle its own name initialization while sharing the sayName() method:

abstract class BaseUser {
    // Subclasses must provide their own implementation of this property
    protected abstract val name: Name

    // Shared method remains in the base class
    fun sayName() {
        println(name.fakeName)
    }
}

// No-argument User class
class User : BaseUser() {
    override val name: Name = NameGenerator()
}

// Suggestion-based User class (rename if you want to keep it distinct)
class SuggestionUser(suggestion: String) : BaseUser() {
    override val name: Name = NameGenerator(suggestion)
}

How this works:

  • BaseUser declares an abstract name property, forcing each subclass to define how it's initialized.
  • Both subclasses override name with their specific NameGenerator call, while inheriting the sayName() method for free.

Either approach will eliminate your duplicate code and keep things clean. If the two initialization styles are just variations of the same user type, Approach 1 is more concise. If they're likely to grow into distinct types later, Approach 2 gives you more flexibility.

内容的提问来源于stack exchange,提问作者Albert Gao

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.20 11:28:25