在大型项目中,找到具有固定“模式”的类并不罕见,这些模式指定了一个类应该如何工作,我们称这些类为“标记类(tagged class)”,因为它们包含指定其操作模式的标记。它们存在许多问题,而这些问题大多源于不同模式的不同职责在同一层级中相互争夺,尽管它们通常是可以区分的。例如,在下面代码块中,我们可以让这个类来测试 value 是否满足某个条件,这个例子是简单的,但它是一个来自大型项目的真实案例:
我们不一定需要使用密封类。我们可以使用 abstract 来代替,但是密封禁止在该文件之外定义任何子类。正因如此,如果我们在 when 中涵盖了其所有子类型,我们就不需要添加 else 分支,因为它能保证是齐全的。利用这个优势,我们可以很容易地添加新功能,并且知道不会忘记在这些 when 语句中去包含它们。
这是一种很便利的方式,可以定义不同模式下具有不同的行为操作。例如,我们可以使用 when 将 reversed 定义为扩展函数,而不用在所有的子类中定义这个函数。新的功能可以以这种方式添加到密封类中,甚至可以作为扩展函数:
sealed class ValueMatcher<T> {
abstract fun match(value: T): Boolean
class Equal<T>(val value: T) : ValueMatcher<T>() {
override fun match(value: T): Boolean =
value == this.value
}
class NotEqual<T>(val value: T) : ValueMatcher<T>() {
override fun match(value: T): Boolean =
value != this.value
}
class EmptyList<T>() : ValueMatcher<T>() {
override fun match(value: T) =
value is List<*> && value.isEmpty()
}
class NotEmptyList<T>() : ValueMatcher<T>() {
override fun match(value: T) =
value is List<*> && value.isNotEmpty()
}
}
fun <T> ValueMatcher<T>.reversed(): ValueMatcher<T> =
when (this) {
is ValueMatcher.EmptyList -> ValueMatcher.NotEmptyList<T>()
is ValueMatcher.NotEmptyList -> ValueMatcher.EmptyList<T>()
is ValueMatcher.Equal -> ValueMatcher.NotEqual(value)
is ValueMatcher.NotEqual -> ValueMatcher.Equal(value)
}
sealed class WorkoutState
class PrepareState(val exercise: Exercise) :
WorkoutState()
class ExerciseState(val exercise: Exercise) :
WorkoutState()
object DoneState : WorkoutState()
fun List<Exercise>.toStates(): List<WorkoutState> =
flatMap { exercise ->
listOf(PrepareState(exercise),
ExerciseState(exercise))
} + DoneState
class WorkoutPresenter( /*...*/ ) {
private var state: WorkoutState = states.first()
//...
}
private var state: WorkoutState by
Delegates.observable(states.first()) { _, _, _ ->
updateView()
}