演習: クラスとコレクション

1. 始める前に

このパスウェイでは、ジェネリクス、さまざまなクラスの型、コレクション、高階関数について学習しました。学んだことを実践するために、あなたはチームによる新しいイベント トラッキング アプリの改善を手伝います。各ステップの手順で、アプリの現在の状態と、完了するタスクの詳細が説明されています。

Kotlin のプレイグラウンドを使用して演習に取り組むことをおすすめします。

前提条件

  • Compose を用いた Android アプリ開発の基礎コースのユニット 3 パスウェイ 1 と、それ以前のパスウェイを完了していること。
  • Kotlin プログラミング言語の基本(クラス、オブジェクト、コレクション、高階関数など)に精通していること

必要なもの

2. アプリの概要

あなたは、イベント トラッキング アプリ チームに新たに加わったソフトウェア エンジニアです。ユーザーがイベントをトラッキングできるようにするアプリを開発しています。チームにより、アプリの機能を構築するためのタスクが割り当てられます。

各タスクの最後で、あなたの解決策と提示される解決策を比較してみてください。目的の機能を実現する方法は複数あるため、あなたのコードが提示されている解決策のコードと完全に一致していなくても問題ありません。

前のタスクで提示される解決策のコードを、次のタスクの出発点のコードとして使用し、共通の出発点からタスクを開始できるようにします。

3. タスク 1

別のソフトウェア エンジニアがすでにアプリの大まかな作業を完了していて、あなたはその詳細を実装するタスクを任されたとします。

あなたは、Event クラスを実装する必要があります。このクラスは、ユーザーが入力したイベントの詳細を保持するために使用されます(ヒント: このクラスではメソッドの定義やアクションの実行は不要です)。

このタスクでは、Event という名前のデータクラスを作成する必要があります。

このクラスのインスタンスは以下を保存できます。

  • イベントのtitle(タイトル、文字列)。
  • イベントのdescription(説明、文字列 で null でもよい)。
  • イベントのdaypart(時間帯、文字列)。トラッキングするのは、イベントが始まった時間帯(Morning、Afternoon、Evening のいずれか)のみです。
  • イベントのduration(分単位の時間、整数)。

続行する前に、自分でコードを記述してみてください。

そのコードを使用して、次の情報でインスタンスを作成します。

  • タイトル: Study Kotlin(Kotlin の学習)
  • 説明: Commit to studying Kotlin at least 15 minutes per day.(1 日に 15 分以上 Kotlin を学習することを約束します。)
  • 時間帯: Evening(夜)
  • 期間: 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」などの値を使用する開発者もいます。

これが、多くの問題につながりました。

ここでのタスクは、リファクタリングを行ってこの問題を修正することです。リファクタリングは、その機能を変更せずにコードを改善する処理です。これには、ロジックの簡素化や、繰り返しコードの別の関数への移動などがあります。

この問題を修正するため一部の個別の値をモデル化するには、どの型のクラスを使用すればよいでしょうか?

チームは、あなたに時間帯コードを enum(列挙型)クラスを使用するように変更してもらいたいと考えています。enum クラスを使用すれば、チームメイトが時間帯を指定する際に提示される時間帯値のいずれかを選択することとなるため、この種の問題は回避できます。

この enum クラスには Daypart という名前を付けます。含まれる値は次の 3 つです。

  • MORNING
  • AFTERNOON
  • EVENING

この enum クラスを、あなたならどのように作成しますか?

Event クラスをリファクタリングして使用できるようにするには、どうすればよいですか?

先に進む前に、自分で解決策をコーディングしてみてください。

[次へ] をクリックすると、解決策のコードが提示されます。

6. タスク 2 の解決策

enum class Daypart {
    MORNING,
    AFTERNOON,
    EVENING,
}

リファクタリングされた Event データクラスで enum クラスが使用されるようになりました。

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)

現時点のコードでは、大量のイベントが生成されるだけでなく、それぞれのイベントに独自の変数が必要になります。生成されるイベントが増えるに従い、すべてのイベントをトラッキングすることが困難になります。このアプローチでは、ユーザーの作成したイベントの数を確認することが非常に困難となるでしょう。

このように保存されたイベントを整理する方法として、より優れた方法はないでしょうか?

すべてのイベントを 1 つの変数に格納するにはどのような方法がありますか(: イベントがさらに追加される可能性があるため、柔軟性が必要になります。また、変数に格納されているイベントの数を効率的に返す必要もあります)?

どのクラスまたはデータ型を使用しますか?イベントをさらに追加する方法として何がありますか?

では、この機能を自分で実装してみましょう。[次へ] をクリックして解決策を表示する前に、自分でコードを記述してみてください。

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

チームは、あなたが設計したデータクラスを好んで使用していますが、文字列のイベント期間を必要とするたびにコードを記述し直すことが手間であると感じています。

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}")

[次へ] をクリックすると、解決策が提示されます。

16. タスク 7 の解決策

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

17. その他の演習

Kotlin 言語に関するその他の演習については、JetBrains Academy の Kotlin Core トラックをご覧ください。特定のトピックに移動するには、ナレッジマップに移動して、このトラックで扱っているトピックの一覧を確認してください。