如何在Kotlin中实现模板方法设计模式?

5
考虑以下问题:
我们有一个具有抽象方法的“Base”类。现在,我们希望强制执行每个此方法的覆盖将执行某些参数检查或其他繁琐工作。我们希望此参数检查在所有覆盖中都是相同的。一种解决方案是将此行为包装在调用抽象方法的非抽象方法中:
abstract class Base
{
    fun computeSomething(argument: Int): Int
    {
        require(argument > 0) // Some intricate checking
        return execute(argument)
    }

    // Pure functionality, assuming correct arguments
    // Ideally, this would be private. 
    protected abstract fun execute(validArgument: Int): Int
}

class Derived1: Base()
{
    override fun execute(validArgument: Int): Int =
        validArgument * validArgument
}

class Derived2: Base()
{
    override fun execute(validArgument: Int): Int =
        validArgument * validArgument * validArgument
}


fun main(args: Array<String>)
{
    val d = Derived1()
    println(d.computeSomething(23))
}

我希望将Base :: execute 设为私有,以便不会在未检查其参数的情况下意外调用Base 的任何子类。但是,这是不可能的:

错误:(9,5)Kotlin:修饰符“ private”与“ abstract”不兼容

Kotlin的protected 比Java的protected 更好,因为它只使函数对子类可访问,但理想情况是private 。那么,有没有正确的方法来实现此模式?另外,出于好奇,private abstract 的不兼容性是语言设计的故意选择吗?

我不会 Kotlin,但这个错误看起来像是 computeSomething 不是 public 的。 - mlecz
你不能使用“protected final”吗?这样它就可以被子类访问,但不能被覆盖。 - NSimon
@NSimon 实际上我希望它被覆盖。我不希望它被“调用”。 - Martin Drozdik
2个回答

2
一个函数被覆盖却不能从该类中调用是没有意义的。通过覆盖该函数,您拥有它的实现,因此可以调用它。
您可以通过将执行逻辑与派生类分离来实现您想要的功能,这可以通过以下几种方式完成:
  • 您可以使用lambda,并将其传递给Base的构造函数
  • 您可以创建一个接口实现来处理执行逻辑
通过在构造函数中将它们传递给Base类,您正在将它们的所有权转移给该类。

1
我同意 lambda 是一个很好的解决方案。但是,我不同意私有方法被覆盖是没有意义的。我的示例代码清楚地说明了这一点。 - Martin Drozdik

1
你可以让他们传递一个对象来检查值是否符合调用此方法的要求。 例如:
abstract class Base
{
    fun computeSomething(argument: Int): Int
    {
        return execute(ValueCheck(argument))
    }

    protected abstract fun execute(validArgument: ValueCheck)

    protected class ValueCheck(a: Int)
    {
        private val value = checkValue(a)

        private fun checkValue(argument: Int): Int
        {
            require(argument > 0)
            return argument
        }

        fun get() = value
    }
}

当派生类想要调用execute时,它必须传递一个在创建实例时检查值的对象。

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