实践:类和集合

1. 开始之前

在本路径中,您学习了泛型、不同类型的类、集合和高阶函数。为了练习您所学到的知识,您将帮助您的团队改进他们新的事件跟踪应用。每个步骤中的说明都会描述应用的当前状态以及您需要完成的任务。

建议您使用 Kotlin Playground 来解决这些练习。

前提条件

  • 完成 Android Basics with Compose 课程的 第 3 单元第 1 路径 及之前的路径。
  • 熟悉 Kotlin 编程语言的基础知识,包括类、对象、集合和高阶函数。

您需要准备的

2. 应用概述

您是事件跟踪应用团队的最新软件工程师。此应用的目的是允许用户跟踪其事件。您的团队将为您分配任务,以帮助构建应用的功能。

完成每个任务后,您应该将您的解决方案与提供的解决方案进行比较。实现所需功能的方法有很多种,因此如果您的代码与提供的解决方案代码不完全匹配,请不必担心。

使用上一个任务中提供的解决方案代码作为下一个任务的起始代码,以便从一个共同的起点开始。

3. 任务 1

另一位软件工程师已经完成了应用的一些高级工作,而您的任务是实现细节。

您需要实现 Event 类。此类用于存储用户输入的事件详细信息。(提示:此类不需要定义任何方法或执行任何操作。)

对于此任务,您需要创建一个名为 Event 的数据类。

此类的实例应该能够存储

  • 事件标题,类型为字符串。
  • 事件描述,类型为字符串(可以为 null)。
  • 事件的一天中的时段,类型为字符串。我们只需要跟踪事件是发生在上午、下午还是晚上。
  • 事件持续时间,以分钟为单位,类型为整数。

在继续之前,请尝试自己编写代码。

使用您的代码,根据以下信息创建一个实例

  • 标题:学习 Kotlin
  • 描述:每天至少花 15 分钟学习 Kotlin。
  • 一天中的时段:晚上
  • 持续时间: 15

尝试打印您的对象,以验证您是否获得以下输出

Event(title=Study Kotlin, description=Commit to studying Kotlin at least 15 minutes per day., daypart=Evening, durationInMinutes=15)

完成任务或尽力尝试后,请点击下一步查看我们的代码实现。

4. 任务 1 解决方案

您的解决方案应与以下代码类似

data class Event(
    val title: String,
    val description: String? = null,
    val daypart: String,
    val durationInMinutes: Int,
)

5. 任务 2

为了让项目按计划进行,您的经理决定使用我们提供的数据类代码。

您的团队成员使用了一段时间的 Event 类后,资深队友意识到使用字符串来表示一天中的时段并不理想。

有些开发者存储了“Morning”值,有些使用了“morning”,还有些使用了“MORNING”。

这导致了许多问题。

您的任务是通过进行重构来解决此问题。重构是在不改变代码功能的情况下改进代码的过程。一些例子包括简化逻辑或将重复的代码移至单独的函数中。

可以使用哪种类型的类来建模一组有限的离散值,以帮助解决此问题?

您的团队希望您将一天中时段的代码更改为使用枚举类。通过使用枚举类,您的队友将被迫选择提供的一天中时段值之一,从而防止出现此类问题。

枚举类应命名为 Daypart。它应该包含三个值

  • MORNING
  • AFTERNOON
  • EVENING

您将如何创建此枚举类?

您将如何重构您的 Event 类来使用它?

在继续之前,请尝试现在编写您的解决方案代码。

点击下一步查看我们的代码实现。

6. 任务 2 解决方案

enum class Daypart {
    MORNING,
    AFTERNOON,
    EVENING,
}

重构后的 Event 数据类现在使用枚举类

data class Event(
    val title: String,
    val description: String? = null,
    val daypart: Daypart,
    val durationInMinutes: Int,
)

7. 任务 3

您的同事很高兴使用重构后的 Daypart,但他们还有其他问题。

以下代码是他们当前创建和存储用户事件的方式。

val event1 = Event(title = "Wake up", description = "Time to get up", daypart = Daypart.MORNING, durationInMinutes = 0)
val event2 = Event(title = "Eat breakfast", daypart = Daypart.MORNING, durationInMinutes = 15)
val event3 = Event(title = "Learn about Kotlin", daypart = Daypart.AFTERNOON, durationInMinutes = 30)
val event4 = Event(title = "Practice Compose", daypart = Daypart.AFTERNOON, durationInMinutes = 60)
val event5 = Event(title = "Watch latest DevBytes video", daypart = Daypart.AFTERNOON, durationInMinutes = 10)
val event6 = Event(title = "Check out latest Android Jetpack library", daypart = Daypart.EVENING, durationInMinutes = 45)

他们创建了许多事件,并且每个事件当前都需要一个单独的变量。随着创建的事件越来越多,跟踪所有事件变得越来越困难。使用这种方法,确定用户安排了多少个事件会有多困难?

您能想到一种更好的方法来组织这些事件的存储吗?

您可以用什么方式将所有事件存储在一个变量中?(注意:它必须是灵活的,因为可能会添加更多事件。它还需要高效地返回存储在该变量中的事件数量。)

您会使用哪种类或数据类型?有一种方法可以添加更多事件是什么?

现在轮到您来实现此功能了。在点击下一步查看我们的解决方案之前,请尝试编写代码。

8. 任务 3 解决方案

val events = mutableListOf<Event>(event1, event2, event3, event4, event5, event6)

9. 任务 4

您的经理很满意应用的进展,但决定用户应该能够根据事件持续时间查看其短时事件的摘要。例如,“您有 5 个短时事件。”

“短时”事件是指持续时间少于 60 分钟的事件。

使用上一个任务解决方案中的 events 变量代码,您将如何实现此结果?

点击下一步继续查看我们的解决方案。

10. 任务 4 解决方案

有多种方法可以完成此任务,以下是我们确定的方法

val shortEvents = events.filter { it.durationInMinutes < 60 }
println("You have ${shortEvents.size} short events.")

11. 任务 5

您的队友很满意应用的进展,但他们希望用户能够查看所有事件及其一天中时段的摘要。

输出应类似于

Morning: 3 events
Afternoon: 4 events
Evening: 2 events

使用上一步中的 events 变量代码,您如何实现此结果?

点击下一步查看解决方案代码。

12. 任务 5 解决方案

以下是我们的解决方案,但也接受其他变体。

val groupedEvents = events.groupBy { it.daypart }
groupedEvents.forEach { (daypart, events) ->
    println("$daypart: ${events.size} events")
}

13. 任务 6

目前,您的同事通过使用索引找到并打印最后一个项目。使用的代码是:println("Last event of the day: ${events[events.size - 1].title}")

您的经理建议查阅Kotlin 文档,寻找可以简化此代码的函数。

您找到了什么函数?

尝试使用它来确认您打印出相同的结果。

点击下一步查看解决方案。

14. 任务 6 解决方案

println("Last event of the day: ${events.last().title}")

15. 任务 7

15. 任务 7

val durationOfEvent = if (events[0].durationInMinutes < 60) {
        "short"
    } else {
        "long"
    }
println("Duration of first event of the day: $durationOfEvent")

您的团队喜欢您设计的数据类,但发现每次需要将事件持续时间作为字符串时都编写代码很重复

虽然您可以通过直接向类添加方法来解决这种重复问题,但这并不理想,因为其他团队已经在他们的应用中开始使用您的事件类。如果类发生变化,他们将需要重新测试所有代码,以确保您的更改不会破坏任何内容。

在不直接更改数据类的情况下,您如何编写一个扩展属性,使其返回与上述代码相同的值?

println("Duration of first event of the day: ${events[0].durationOfEvent}")

正确实现后,您将能够使用以下代码,它将打印出与本任务开始时显示的代码相同的消息。

点击下一步继续查看解决方案。

val Event.durationOfEvent: String
    get() = if (this.durationInMinutes < 60) {
        "short"
    } else {
        "long"
    }

16. 任务 7 解决方案

17. 额外练习