其他分享
首页 > 其他分享> > Kotlin 中的密封类 优于 带标签的类

Kotlin 中的密封类 优于 带标签的类

作者:互联网

在之前的文章中我也分析过 Sealed Classes 原理,以及 Google 和很多开源项目为什么都在大量的使用它,如果你对 Sealed Classes 还不是很了解,可以前往查看 Kotlin Sealed 是什么?为什么 Google 都在用 主要内容如下:

而今天这篇文章,我们主要从类层次结构来讨论一下 Sealed Classes(密封类) 和 Tagged Classes(标记类)的优缺点。在开始分析之前,我们先介绍一下什么是 Tagged Classes(标记类)以及都有那些缺点。

Tagged Classes 是什么

在一个类中包含一个指示操作的标记字段或者特征,方便在它们之间切换的类称为 Tagged Classes(标记类),在 Effective Java 中也指出了 Tagged Classes 存在很多问题,这里引用 Effective Java Item 23 中的一个案例来分析 Tagged Classes 存在的问题,这里用 Kotlin 重写了。

class Figure(
    // 这个标签字段:用来表示图形的形状
    val shape: Shape,
    // 这个字段用于圆形
    val radius: Double = 0.0,
    // 这两个字段用于矩形
    val length: Double = 0.0,
    val width: Double = 0.0
) {
    // 定义了两个形状 矩形、圆形
    enum class Shape {
        RECTANGLE, CIRCLE
    }

    // 计算当前图形的面积
    fun area(): Double = when (shape) {
        Shape.RECTANGLE -> length * width
        Shape.CIRCLE -> Math.PI * (radius * radius)
        else -> throw AssertionError(shape)
    }

    companion object {
        fun createRectangle(radius: Double) {
            Figure(
                shape = Shape.RECTANGLE,
                radius = radius
            )
        }

        fun createCircle(length: Double, width: Double = 0.0) {
            Figure(
                shape = Shape.CIRCLE,
                length = length,
                width = width
            )
        }
    }
}

正如你所见,代码中包含了很多模板代码,包括标记字段、切换语句、枚举等等,在一个类中包含了很多不同的操作,如果以后增加新的操作,有需要增加新的标记,实际情况这样的代码在项目中非常的常见,主要存在以下几个问题:

那么有没有很好的替换方案,可以解决以上所有的问题,而且还可以在不修改原有的代码结构基础上增加新的图形,这就需要用到类的层次结构。

类的层次结构

无论是 Java 还是 Kotlin 我们都会使用类的层次结构代替标记类,而在 Kotlin 中我们常用 Sealed Classes 表示受限制的类层次结构, 在之前的文章 Kotlin Sealed 是什么? 中已经详细分析过 Sealed Classes。接下来一起来看一下如何使用 Sealed Classes 优化上面的代码。

sealed class Figure {
    abstract fun area(): Double

    class Rectangle(val length: Double, val width: Double) : Figure() {
        override fun area(): Double = length * width
    }

    class Circle(val radius: Double) : Figure() {
        override fun area(): Double = Math.PI * (radius * radius)
    }
}

正如你所见,代码简洁干净了很多,不包含模板代码,并且类之间的职责分明,提高了代码的灵活性,完美的解决了上述所有的缺点。每个类中不包含无关的字段,同时在类中添加新的参数,并不会影响其他类。

如果我们需要增加新的图形,只需要新增加一个类即可,并不会破坏原有的代码结构,例如这里我们增加一个球形。

class Ball(val radius: Double) : Figure() {
    override fun area(): Double = 4.0 * Math.PI * Math.pow(radius, 2.0)
}

不仅仅如此,Sealed Classes 结合 when 表达式一起使用会更加的方便,when 语句下的所有分支可以通过快捷键 Mac/Win/Linux:Alt + Enter 自动生成,如下所示。

fun Figure.Valida() {
    when (this) {
        is Figure.Ball -> {
            println("I am Ball")
            area()
        }
        is Figure.Circle -> {
            println("I am Circle")
            area()
        }
        is Figure.Rectangle -> {
            println("I am Rectangle")
            area()
        }
    }
}

在 Effective Java 中也说明了 Tagged classes(标记类)很少有适合的场景,但是往往在开发过程中,为了快速的开发一个功能,往往会忽略它所带来的影响,但是我们在做优化的时候,遇到这种 Tagged classes 是否可以考虑使用类的层次结构来代替,如果是 Kotlin 建议使用 Sealed Classes。

参考文章


最后推荐我一直在更新维护的项目和网站:

标签:Figure,Kotlin,密封,Classes,radius,标签,Sealed,Double
来源: https://blog.csdn.net/long71751380/article/details/118014679