什么是密封接口?

密封接口(sealed interface)是kotlin 1.5引进的一个新特性,它能够让咱们界说一个限制性的类层次结构,也就是说,咱们能够在编译时就知道一个密封接口有哪些或许的子类型。这样,咱们就能够更好地控制承继关系,防止呈现意外的子类型。

密封接口与密封类(sealed class)类似,都能够用来表明一组有限的或许性。可是,密封类只能有一个实例,而密封接口的子类型能够有多个实例。此外,密封类只能被类承继,而密封接口能够被类和枚举类(enum class)完成。

要声明一个密封接口,咱们需求在interface要害字前加上sealed修饰符:

sealed interface Error // 密封接口

一个密封接口能够有笼统或默许完成的办法,也能够有特点:

sealed interface Shape { // 密封接口
    val area: Double // 特点
    fun draw() // 笼统办法
    fun printArea() { // 默许完成办法
        println("The area is $area")
    }
}

密封接口的长处

运用密封接口有以下几个长处:

  • 类型安全:由于密封接口的子类型是固定的,咱们能够在编译时就查看是否覆盖了一切或许的状况。这样,咱们就不会遗失某些分支或许处理过错的类型。
  • 可读性:运用密封接口能够让咱们清楚地看到一个类型有哪些变种。这样,咱们就能够更简单地理解和保护代码。
  • 灵活性:运用密封接口能够让咱们界说更多样化的子类型。咱们能够运用数据类(data class),目标(object),普通类(class),或许另一个密封类(sealed class)作为子类型。咱们还能够在不同的文件或模块中界说子类型。
  • 表达力:运用密封接口能够让咱们运用多态(polymorphism)和承继(inheritance)来完成更复杂和优雅的规划形式。

密封接口在规划形式中的运用

规划形式是一些通过验证和总结的处理特定问题的代码结构和技巧。运用规划形式能够让咱们编写出更高效,更可复用,更易扩展的代码。

下面,咱们将介绍几种常见的规划形式,并展现怎么运用密封接口来完成它们。

战略形式

战略形式(Strategy Pattern)是一种行为型规划形式,它能够让咱们在运行时依据不同的状况选择不同的算法或战略。这样,咱们就能够将算法的界说和运用别离,进步代码的灵活性和可保护性。

要完成战略形式,咱们能够运用密封接口来界说一个战略的笼统,然后运用不同的子类型来完成具体的战略。例如,咱们能够界说一个排序战略的密封接口,然后运用不同的排序算法作为子类型:

sealed interface SortStrategy { // 密封接口
    fun sort(list: List<Int>): List<Int> // 笼统办法
}
object BubbleSort : SortStrategy { // 目标
    override fun sort(list: List<Int>): List<Int> {
        // 完成冒泡排序
    }
}
object QuickSort : SortStrategy { // 目标
    override fun sort(list: List<Int>): List<Int> {
        // 完成快速排序
    }
}
object MergeSort : SortStrategy { // 目标
    override fun sort(list: List<Int>): List<Int> {
        // 完成归并排序
    }
}

然后,咱们能够界说一个上下文类(Context Class),它能够持有一个战略的引用,并依据需求切换不同的战略:

class Sorter(var strategy: SortStrategy) { // 上下文类
    fun sort(list: List<Int>): List<Int> {
        return strategy.sort(list) // 调用战略的办法
    }
}

最终,咱们能够在客户端代码中运用上下文类来履行不同的战略:

fun main() {
    val list = listOf(5, 3, 7, 1, 9)
    val sorter = Sorter(BubbleSort) // 创立上下文类,并指定初始战略
    println(sorter.sort(list)) // 运用冒泡排序
    sorter.strategy = QuickSort // 切换战略
    println(sorter.sort(list)) // 运用快速排序
    sorter.strategy = MergeSort // 切换战略
    println(sorter.sort(list)) // 运用归并排序
}

运用密封接口完成战略形式的长处是:

  • 咱们能够在编译时就知道有哪些可用的战略,防止呈现无效或未知的战略。
  • 咱们能够运用数据类,目标,普通类或密封类作为子类型,依据不同的战略需求界说不同的特点和办法。
  • 咱们能够在不同的文件或模块中界说子类型,进步代码的模块化和可读性。

假如运用java完成战略形式,咱们或许需求界说一个接口来表明战略,然后运用不同的类来完成接口:

interface SortStrategy { // 接口
    List<Integer> sort(List<Integer> list); // 笼统办法
}
class BubbleSort implements SortStrategy { // 类
    @Override
    public List<Integer> sort(List<Integer> list) {
        // 完成冒泡排序
    }
}
class QuickSort implements SortStrategy { // 类
    @Override
    public List<Integer> sort(List<Integer> list) {
        // 完成快速排序
    }
}
class MergeSort implements SortStrategy { // 类
    @Override
    public List<Integer> sort(List<Integer> list) {
        // 完成归并排序
    }
}

然后,咱们也需求界说一个上下文类来持有和切换战略:

class Sorter { // 上下文类
    private SortStrategy strategy; // 战略引用
    public Sorter(SortStrategy strategy) { // 结构函数
        this.strategy = strategy;
    }
    public void setStrategy(SortStrategy strategy) { // 设置战略办法
        this.strategy = strategy;
    }
    public List<Integer> sort(List<Integer> list) {
        return strategy.sort(list); // 调用战略的办法
    }
}

最终,咱们也能够在客户端代码中运用上下文类来履行不同的战略:

public static void main(String[] args) {
    List<Integer> list = Arrays.asList(5, 3, 7, 1, 9);
    Sorter sorter = new Sorter(new BubbleSort()); // 创立上下文类,并指定初始战略
    System.out.println(sorter.sort(list)); // 运用冒泡排序
    sorter.setStrategy(new QuickSort()); // 切换战略
    System.out.println(sorter.sort(list)); // 运用快速排序
    sorter.setStrategy(new MergeSort()); // 切换战略
    System.out.println(sorter.sort(list)); // 运用归并排序
}

运用java完成战略形式的缺陷是:

  • 咱们不能在编译时就知道有哪些可用的战略,由于任何类都能够完成接口。
  • 咱们只能运用类作为子类型,不能运用数据类或目标。
  • 咱们必须在同一个包中界说子类型,不能在不同的文件或模块中。

拜访者形式

拜访者形式(Visitor Pattern)是一种行为型规划形式,它能够让咱们在不修正原有类结构的状况下,为类增加新的操作或功用。这样,咱们就能够将数据结构和操作别离,进步代码的扩展性和复用性。

要完成拜访者形式,咱们能够运用密封接口来界说一个元素(Element)的笼统,然后运用不同的子类型来完成具体的元素。例如,咱们能够界说一个表达式(Expression)的密封接口,然后运用不同的子类型来表明不同的表达式:

sealed interface Expression { // 密封接口
    fun accept(visitor: Visitor): Any // 笼统办法,承受拜访者
}
data class Number(val value: Int) : Expression { // 数据类
    override fun accept(visitor: Visitor): Any {
        return visitor.visitNumber(this) // 调用拜访者的办法
    }
}
data class Sum(val left: Expression, val right: Expression) : Expression { // 数据类
    override fun accept(visitor: Visitor): Any {
        return visitor.visitSum(this) // 调用拜访者的办法
    }
}
data class Product(val left: Expression, val right: Expression) : Expression { // 数据类
    override fun accept(visitor: Visitor): Any {
        return visitor.visitProduct(this) // 调用拜访者的办法
    }
}

然后,咱们能够界说一个拜访者(Visitor)的接口,它能够为每种元素提供一个拜访办法:

interface Visitor { // 拜访者接口
    fun visitNumber(number: Number): Any // 拜访数字表达式
    fun visitSum(sum: Sum): Any // 拜访加法表达式
    fun visitProduct(product: Product): Any // 拜访乘法表达式
}

最终,咱们能够界说不同的拜访者完成类,它们能够为元素提供不同的操作或功用。例如,咱们能够界说一个求值(Evaluate)拜访者,它能够核算表达式的值:

class Evaluate : Visitor { // 求值拜访者
    override fun visitNumber(number: Number): Any {
        return number.value // 返回数字本身
    }
    override fun visitSum(sum: Sum): Any {
        return (sum.left.accept(this) as Int) + (sum.right.accept(this) as Int) // 返回左右子表达式之和
    }
    override fun visitProduct(product: Product): Any {
        return (product.left.accept(this) as Int) * (product.right.accept(this) as Int) // 返回左右子表达式之积
    }
}

咱们还能够界说一个打印(Print)拜访者,它能够打印表达式的字符串表明:

class Print : Visitor { // 打印拜访者
    override fun visitNumber(number: Number): Any {
        return number.value.toString() // 返回数字的字符串
    }
    override fun visitSum(sum: Sum): Any {
        return "(${sum.left.accept(this)}) + (${sum.right.accept(this)})" // 返回加法的字符串
    }
    override fun visitProduct(product: Product): Any {
        return "(${product.left.accept(this)}) * (${product.right.accept(this)})" // 返回乘法的字符串
    }
}

运用密封接口完成拜访者形式的长处是:

  • 咱们能够在编译时就知道有哪些可用的元素,防止呈现无效或未知的元素。
  • 咱们能够运用数据类,目标,普通类或密封类作为子类型,依据不同的元素需求界说不同的特点和办法。
  • 咱们能够在不同的文件或模块中界说子类型,进步代码的模块化和可读性。
  • 咱们能够在不修正元素类的状况下,为它们增加新的拜访者和操作。

假如运用java完成拜访者形式,咱们或许需求界说一个笼统类来表明元素,然后运用不同的子类来承继元素:

abstract class Expression { // 笼统类
    public abstract Object accept(Visitor visitor); // 笼统办法,承受拜访者
}
class Number extends Expression { // 子类
    private int value; // 特点
    public Number(int value) { // 结构函数
        this.value = value;
    }
    public int getValue() { // 获取特点值办法
        return value;
    }
    @Override
    public Object accept(Visitor visitor) {
        return visitor.visitNumber(this); // 调用拜访者的办法
    }
}
class Sum extends Expression { // 子类
    private Expression left; // 特点
    private Expression right; // 特点
    public Sum(Expression left, Expression right) { // 结构函数
        this.left = left;
        this.right = right;
    }
    public Expression getLeft() { // 获取特点值办法
        return left;
    }
    public Expression getRight() { // 获取特点值办法
        return right;
    }
    @Override
    public Object accept(Visitor visitor) {
        return visitor.visitSum(this); // 调用拜访者的办法
    }
}
class Product extends Expression { // 子类
    private Expression left; // 特点
    private Expression right; // 特点
    public Product(Expression left, Expression right) { // 结构函数
        this.left = left;
        this.right = right;
    }
    public Expression getLeft() { // 获取特点值办法
        return left;
    }
    public Expression getRight() { // 获取特点值办法
        return right;
    }
    @Override
    public Object accept(Visitor visitor) {
        return visitor.visitProduct(this); // 调用拜访者的办法
    }
}

关注大众号:Android老皮!!!欢迎大家来找我讨论沟通

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。