解锁 Kotlin 中密封类(Seal Class)的能力:设计模式与代码组织的优化

news2024/9/28 23:37:32

解锁 Kotlin 中密封类(Seal Class)的能力:设计模式与代码组织的优化

多年来,我参与了多个项目,深知编写清晰、易维护代码的价值。最近在一个涉及大量数据类型处理的项目中,我发现使用密封类极大地提高了数据的组织和管理效率。此外,密封类有助于强制类型安全,从而使代码更加健壮,减少了错误的可能性。在本文中,我将分享在 Kotlin 中使用密封类的经验,以及如何利用它们实现设计模式和优化代码组织。

什么是密封类?

密封类是 Kotlin 中使用sealed关键字标记的类。它用于定义一组封闭的子类。它允许您在受限的类层次结构中定义预定义且有限的子类。密封类的子类在密封类本身内部定义,每个子类必须声明为 innerdataclass,不允许使用其他修饰符。

密封类的语法:

Kotlin 中密封类的语法如下:

sealed class SealedClassName {
    // Subclasses
    class SubclassName1 : SealedClassName()
    class SubclassName2 : SealedClassName()
    // ...
}

密封类的用途:
密封类在许多情况下非常有用,特别是当您有一组固定的可能需要表示的类时。以下是一些常见的密封类用例:

表示操作的结果:
密封类的一个常见用例是表示操作的结果。例如,我们可以定义一个名为 Result 的密封类,它有两个子类:Success Error

sealed class Result {
    data class Success(val data: String) : Result()
    data class Error(val message: String) : Result()
}

通过这样的定义,我们可以使用 when 表达式处理 Result 的所有可能情况,如下所示:

fun handleResult(result: Result) {
    when(result) {
        is Result.Success -> println(result.data)
        is Result.Error -> println(result.message)
    }
}

状态机:
密封类的另一个常见用例是表示状态机的状态。例如,我们可以定义一个名为 State 的密封类,其子类表示游戏的不同状态。

sealed class State {
    object Initial : State()
    object Running : State()
    object Paused : State()
    object Finished : State()
}

通过这样的定义,我们可以使用 when 表达式处理 State 的所有可能情况,如下所示:

fun handleState(state: State) {
    when(state) {
        is State.Initial -> println("The game is starting...")
        is State.Running -> println("The game is running...")
        is State.Paused -> println("The game is paused...")
        is State.Finished -> println("The game is finished!")
    }
}

处理 UI 状态:
密封类在处理 Android 应用程序中的不同 UI 状态时非常常见。例如,您可以定义一个名为 ViewState 的密封类,其子类表示屏幕的不同 UI 状态。

有了这个定义,我们可以使用 when 表达式处理 State 的所有可能情况,如下所示:

sealed class ViewState {
    object Loading : ViewState()
    data class Success(val data: List<String>) : ViewState()
    data class Error(val message: String) : ViewState()
}

通过这样的定义,我们可以使用 when 表达式处理 ViewState 的所有可能情况,如下所示:

fun handleViewState(viewState: ViewState) {
    when(viewState) {
      is ViewState.Loading -> showLoadingIndicator()
      is ViewState.Success -> showData(viewState.data)
      is ViewState.Error -> showError(viewState.message)
  }
}

以下是更多使用密封类实现设计模式的例子:

状态模式:
状态模式用于表示对象的状态以及根据该状态应执行的行为。密封类可以用于定义对象的不同状态,每个状态都可以有自己的行为。

sealed class State {
    abstract fun handle()
}

object IdleState : State() {
    override fun handle() {
        // Do nothing
    }
}

object ActiveState : State() {
    override fun handle() {
        // Do something when in active state
    }
}

object InactiveState : State() {
    override fun handle() {
        // Do something when in inactive state
    }
}

通过这样的定义,我们可以使用 when 表达式处理对象的所有可能状态并执行相应的行为,如下所示:

fun handleState(state: State) {
    when(state) {
        is IdleState -> state.handle()
        is ActiveState -> state.handle()
        is InactiveState -> state.handle()
    }
}

访问者模式:
访问者模式用于向现有类添加新行为,而无需修改这些类。密封类可用于定义访问者将访问的类的层次结构,每个子类可以实现接受访问者的方法。

sealed class Element {
    abstract fun accept(visitor: Visitor)
}

class ConcreteElementA : Element() {
    override fun accept(visitor: Visitor) {
        visitor.visitConcreteElementA(this)
    }
}

class ConcreteElementB : Element() {
    override fun accept(visitor: Visitor) {
        visitor.visitConcreteElementB(this)
    }
}

interface Visitor {
    fun visitConcreteElementA(element: ConcreteElementA)
    fun visitConcreteElementB(element: ConcreteElementB)
}

通过这样的定义,我们可以通过创建实现 Visitor 接口并重写 Element 的每个子类方法的新类来实现新行为。

工厂模式:
工厂模式用于在不将实例化逻辑暴露给客户端的情况下创建对象。密封类可以用于定义工厂将创建的不同类型的对象。

sealed class ViewState {
    object Loading : ViewState()
    data class Success(val data: List<String>) : ViewState()
    data class Error(val message: String) : ViewState()
}
sealed class Product {
    abstract fun getDescription(): String
}

class ConcreteProductA : Product() {
    override fun getDescription(): String {
        return "This is product A"
    }
}

class ConcreteProductB : Product() {
    override fun getDescription(): String {
        return "This is product B"
    }
}

object ProductFactory {
    fun createProduct(type: String): Product {
        return when(type) {
            "A" -> ConcreteProductA()
            "B" -> ConcreteProductB()
            else -> throw IllegalArgumentException("Unknown product type")
        }
    }
}

通过这样的定义,您可以通过在 ProductFactory 上调用 createProduct 方法来创建新的产品。

工厂方法模式:
工厂方法模式用于创建对象而不指定其具体类型。密封类可以用于定义可以创建的对象类型。

sealed class Animal {
    abstract fun makeSound()
}

class Dog : Animal() {
    override fun makeSound() {
        println("Woof!")
    }
}

class Cat : Animal() {
    override fun makeSound() {
        println("Meow!")
    }
}

object AnimalFactory {
    fun createAnimal(type: String): Animal? {
        return when (type) {
            "dog" -> Dog()
            "cat" -> Cat()
            else -> null
        }
    }
}

通过这样的定义,您可以通过使用适当的类型调用 createAnimal 方法来创建不同类型的动物,如下所示:

val dog = AnimalFactory.createAnimal("dog") // Dog
val cat = AnimalFactory.createAnimal("cat") // Cat
val rabbit = AnimalFactory.createAnimal("rabbit") // null

策略模式:
策略模式用于在运行时选择算法。密封类可以用于定义可选的不同策略。

sealed class SortingStrategy {
    abstract fun sort(array: IntArray)
}

object BubbleSort : SortingStrategy() {
    override fun sort(array: IntArray) {
        // implementation of bubble sort
    }
}

object MergeSort : SortingStrategy() {
    override fun sort(array: IntArray) {
        // implementation of merge sort
    }
}

class Sorter(private var strategy: SortingStrategy) {
    fun setStrategy(strategy: SortingStrategy) {
        this.strategy = strategy
    }

    fun sort(array: IntArray) {
        strategy.sort(array)
    }
}

通过这样的定义,您可以创建一个排序器对象并在运行时设置其策略,如下所示:

val sorter = Sorter(BubbleSort)
sorter.sort(arrayOf(3, 2, 1)) // array is now sorted using bubble sort
sorter.setStrategy(MergeSort)
sorter.sort(arrayOf(3, 2, 1)) // array is now sorted using merge sort

装饰器模式:
装饰器模式用于在不改变对象结构的情况下动态添加功能。密封类可以用于定义基本对象及其装饰器类。

sealed class Coffee {
    abstract fun getCost(): Double
    abstract fun getDescription(): String
}

class BasicCoffee : Coffee() {
    override fun getCost(): Double {
        return 2.0
    }

    override fun getDescription(): String {
        return "Basic coffee"
    }
}

abstract class CoffeeDecorator(val coffee: Coffee) : Coffee()

class Milk(coffee: Coffee) : CoffeeDecorator(coffee) {
    override fun getCost(): Double {
        return coffee.getCost() + 0.5
    }

    override fun getDescription(): String {
        return coffee.getDescription() + ", with milk"
    }
}

class Sugar(coffee: Coffee) : CoffeeDecorator(coffee) {
    override fun getCost(): Double {
        return coffee.getCost() + 0.25
    }

    override fun getDescription(): String {
        return coffee.getDescription() + ", with sugar"
    }
}

通过这样的定义,我们可以通过组合不同的装饰器来创建不同类型的咖啡,如下所示:

val basicCoffee = BasicCoffee()
val coffeeWithMilk = Milk(basicCoffee)
val coffeeWithSugar = Sugar(basicCoffee)
val coffeeWithMilkAndSugar = Sugar(Milk(basicCoffee))

结论:

密封类是 Kotlin 的一项强大特性,可帮助您编写更清晰、更简洁和更富表现力的代码。它们提供了一种定义受限类层次结构的方式,可用于各种用途,包括更好的代码组织和实现设计模式。通过使用密封类,您可以创建更易于阅读和维护的代码,且更容易扩展和修改。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/803789.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

推动中小企业数字化转型,开利网络签约

随着数字经济的发展&#xff0c;大数据、区块链、物联网、AI等新兴数字化技术已成为一种趋势&#xff0c;对于产业园区而言&#xff0c;结合数字化技术形成的“数字园区”理念正逐渐出现在公众视野中。什么是“数字园区”&#xff1f;简单来说&#xff0c;通过对产业园区进行数…

<C语言> 动态内存管理

1.动态内存函数 为什么存在动态内存分配&#xff1f; int main(){int num 10; //向栈空间申请4个字节int arr[10]; //向栈空间申请了40个字节return 0; }上述的开辟空间的方式有两个特点&#xff1a; 空间开辟大小是固定的。数组在申明的时候&#xff0c;必须指定数组的…

使用RunnerGo来简化测试流程

在软件开发过程中&#xff0c;测试是一个重要的环节&#xff0c;需要投入大量时间和精力来确保应用程序或网站的质量和稳定性。但是&#xff0c;随着应用程序变得更加复杂和庞大&#xff0c;传统的测试工具在面对比较繁琐的项目时非常费时费力。这时&#xff0c;一些自动化测试…

MAC电脑设置charles,连接手机的步骤说明(个人实际操作)

目录 一、charles web端设置 1. 安装charles之后&#xff0c;先安装证书 2. 设置 Proxy-Proxy Settings 3. 设置 SSL Proxying 二、手机的设置 1. 安卓 2. ios 资料获取方法 一、charles web端设置 1. 安装charles之后&#xff0c;先安装证书 Help-SSL Proxying-Inst…

高压放大器模块的作用是什么呢

高压放大器模块是一种集成了高压放大器芯片、控制电路、保护电路等多种元件和功能的模块化设备。它可以将输入信号进行放大处理&#xff0c;并输出到负载上&#xff0c;具有高性能、高可靠性、高稳定性等优点。下面安泰电子将详细介绍高压放大器模块的作用&#xff1a; 信号放大…

【LLM】浅析chatglm的sft+p-tuning v2

note GLM将针对不同类型下游任务的预训练目标统一为了自回归填空&#xff0c;结合了混合的注意力机制和新的二维位置编码。本文浅析sft&#xff0c;并基于GLM在广告描述数据集上进行sftp-tuning代码的数据流讲解 文章目录 note零、ChatGLM2模型一、Supervised fine-tuning1. 数…

位图和布隆过滤器+哈希切分思想

文章目录 一.位图(bitset)底层实现: 二.布隆过滤器(bloomFilter)底层实现: 三.哈希切分思想 一.位图(bitset) 位图是一种以一个比特位为数据记录单元的哈希表 ,以无符号整数为key值,采用直接定址法(不存在哈希冲突的问题),其哈希映射函数为 f ( k e y ) k e y ( k e y 的存在…

有关合泰BA45F5260中断的思考

最近看前辈写的代码&#xff0c;发现这样一段代码&#xff1a; #ifdef SUPPORT_RF_NET_FUNCTION if(UART_INT_is_L()) { TmrInsertTimer(eTmrHdlUartRxDelay,TMR_PERIOD(2000),NULL); break; } #endif 其中UART_INT_is_L&am…

【lesson6】Linux下:第一个小程序,进度条代码

文章目录 准备工作sleep问题fflush回车与换行的区别 进度条代码 准备工作 sleep问题 首先我们来看一段代码&#xff1a; 这时候有个 问题这个代码是输出“hello world”还是先sleep三秒&#xff1f; 再来一段代码 这个代码是先sleep三秒还是先输出“hello world”&#xff…

「乐天世界」NFT 作品集

进入「乐天世界」NFT 作品集的迷人世界&#xff0c;这里仿佛就是乐天世界探险主题公园里充满活力的礼品店。 准备好随着想象力的飞跃而沉浸其中吧&#xff0c;因为主题公园里的普通物品已经变得非凡。沉浸在游乐园美食的魔力中&#xff0c;如香脆的玉米热狗、令人垂涎的巧克力蛋…

立创EDA学习

学习树莓派3B的板子发现有个扩展板比较好&#xff0c;自己最好画一个&#xff0c;反正免费。 学习视频&#xff1a;立创EDA&#xff08;专业版&#xff09;电路设计与制作快速入门。 下载专业版&#xff0c;并激活。【分专业版和标准版&#xff0c;专业版也是免费的】 手机…

基于物联网技术的能耗在线监测平台的架构设计与应用

安科瑞 华楠 摘要&#xff1a;围绕工业生产等领域节能降耗实际需求&#xff0c;提出基于物联网的能耗在线监测平台总体方案&#xff0c;面向政府、行业、企业提供能耗管理信息化管理与服务;研究设计能耗监测终端&#xff0c;支持多种工业总线及工业协议&#xff0c;实现电表、…

jenkins执行jmeter时,报Begin size 1 is not equal to fixed size 5

jenkins执行jmeter脚本的时候一直提示如下错误&#xff1a; Tidying up ... Fri Jul 28 17:03:53 CST 2023 (1690535033178) Error generating the report: org.apache.jmeter.report.dashboard.GenerationException: Error while processing samples: Consumer failed wi…

this关键字和同步异步宏认为微任务理解

目录 js面试常见问题&#xff1a;1.this指向 2.闭包定义和作用 3.原型链 4.异步协程 this关键字 this主要有以下几个使用场合。 1&#xff09;全局环境 &#xff08;2&#xff09;构造函数 &#xff08;3&#xff09;对象的方法 避免多层this 避免数组处理方法中的 this 避免回…

RabbitMQ 教程 | RabbitMQ 入门

&#x1f468;&#x1f3fb;‍&#x1f4bb; 热爱摄影的程序员 &#x1f468;&#x1f3fb;‍&#x1f3a8; 喜欢编码的设计师 &#x1f9d5;&#x1f3fb; 擅长设计的剪辑师 &#x1f9d1;&#x1f3fb;‍&#x1f3eb; 一位高冷无情的编码爱好者 大家好&#xff0c;我是 DevO…

VS CODE 20230728

VSCode添加至右键菜单 2.Visual Studio Code(VS Code)中文显示乱码的解决方法 1.按 快捷键 ctrl, 在搜索栏中输入“files:auto Guess Encoding” 勾选 还是乱码

UG\NX二次开发 获取2D制图视图中可见的对象,并获取类型

文章作者:里海 来源网站:https://blog.csdn.net/WangPaiFeiXingYuan 简介: 使用UF_VIEW_ask_visible_objects获取2D制图视图中可见的对象,并获取类型。 下面是将一个六面体以不同的视图投影,获取视图对象和类型的效果。 效果: 1个部件事例,1个体,4条边 1个部件事…

C++,类和对象-多态,制作饮品

#include<iostream> using namespace std;//多态案例&#xff0c;制作饮品class AbstractDrinking { public://煮水virtual void Boil() 0;//冲泡virtual void Brew() 0;//倒入茶杯virtual void PourInCup() 0;//加入辅料virtual void PutSomething() 0;//制作饮品vo…

外文期刊影响因子去哪里查询,如何查询

期刊影响因子(Impact factor&#xff0c;IF)&#xff0c;是代表期刊影响大小的一项定量指标。也就是某刊平均每篇论文的被引用数&#xff0c;它实际上是某刊在某年被全部源刊物引证该刊前两年发表论文的次数&#xff0c;与该刊前两年所发表的全部源论文数之比。那么&#xff0c…