Thực hành: Lớp và Bộ sưu tập

1. Trước khi bắt đầu

Trong lộ trình này, bạn đã tìm hiểu về các giá trị chung, các loại lớp, bộ sưu tập và các hàm bậc cao hơn. Để thực hành những gì bạn đã tìm hiểu, bạn sẽ giúp nhóm của mình cải thiện ứng dụng theo dõi sự kiện mới của họ. Hướng dẫn cho từng bước mô tả trạng thái hiện tại của ứng dụng và tác vụ bạn dự kiến hoàn thành.

Bạn nên sử dụng Kotlin Playground để giải các bài tập này.

Điều kiện tiên quyết

  • Hoàn thành Bài 3 trong lộ trình 1 của khoá học Khái niệm cơ bản về Compose trên Android và các lộ trình trước khoá học đó.
  • Nắm rõ các khái niệm cơ bản về ngôn ngữ lập trình Kotlin, bao gồm lớp, đối tượng, bộ sưu tập và các hàm bậc cao hơn.

Bạn cần có

  • Một máy tính có kết nối Internet
  • Quyền truy cập vào Kotlin Playground

2. Tổng quan về ứng dụng

Bạn là kỹ sư phần mềm mới nhất trong nhóm phát triển ứng dụng theo dõi sự kiện. Mục đích của ứng dụng này là cho phép người dùng theo dõi sự kiện của mình. Nhóm của bạn sẽ giao cho bạn các nhiệm vụ để giúp xây dựng chức năng của ứng dụng.

Khi kết thúc mỗi nhiệm vụ, bạn nên so sánh giải pháp của mình với giải pháp được cung cấp. Có nhiều cách để đạt được chức năng mong muốn, vì vậy, đừng lo lắng nếu mã của bạn không khớp chính xác với mã giải pháp được cung cấp.

Sử dụng mã giải pháp được cung cấp trong nhiệm vụ trước đó làm mã khởi động cho nhiệm vụ tiếp theo để bắt đầu tại một điểm xuất phát chung.

3. Nhiệm vụ 1

Một kỹ sư phần mềm khác đã hoàn thành một số công việc ở cấp cao cho ứng dụng và bạn có nhiệm vụ triển khai thông tin chi tiết.

Bạn cần triển khai lớp Event. Lớp này dùng để lưu giữ thông tin chi tiết về sự kiện mà người dùng đã nhập. (Gợi ý: Lớp này không cần xác định bất kỳ phương thức nào hoặc thực hiện bất kỳ hành động nào.)

Đối với nhiệm vụ này, bạn cần tạo một lớp dữ liệu có tên Event.

Một thực thể của lớp này sẽ có thể lưu trữ:

  • Tiêu đề sự kiện dưới dạng chuỗi.
  • Mô tả sự kiện dưới dạng một chuỗi (có thể có giá trị null).
  • Sự kiện thời gian trong ngày dưới dạng một chuỗi. Chúng ta chỉ cần theo dõi xem sự kiện bắt đầu vào buổi sáng, buổi chiều hay buổi tối.
  • Sự kiện thời lượng tính bằng phút dưới dạng số nguyên.

Trước khi tiếp tục, hãy thử tự viết mã.

Khi dùng mã của bạn, bạn sẽ tạo một thực thể bằng thông tin sau:

  • Tiêu đề: Nghiên cứu Kotlin
  • Nội dung mô tả: Cam kết tìm hiểu về Kotlin ít nhất 15 phút mỗi ngày.
  • Thời gian trong ngày: Buổi tối
  • Thời lượng: 15

Thử in đối tượng để xác minh rằng bạn nhận được kết quả sau:

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

Sau khi hoàn thành nhiệm vụ hoặc đã cố gắng hết sức, hãy nhấp vào Next theo (Tiếp theo) để xem cách chúng tôi mã hoá nhiệm vụ đó.

4. Giải pháp cho Nhiệm vụ 1

Giải pháp của bạn phải tương tự như mã sau:

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

5. Nhiệm vụ 2

Để dự án đi đúng hướng, người quản lý của bạn quyết định sử dụng mã mà chúng tôi cung cấp cho lớp dữ liệu.

Sau khi các thành viên trong nhóm bạn sử dụng lớp Event một thời gian, thành viên nhóm cao cấp đó sẽ nhận ra rằng việc sử dụng chuỗi cho thời gian trong ngày là không lý tưởng.

Một số nhà phát triển lưu trữ giá trị của "Morning" (Buổi sáng), một số nhà phát triển dùng "morning" (buổi sáng) và một số nhà phát triển khác sử dụng giá trị "MORNING" (SÁNG).

Việc này gây ra nhiều sự cố.

Nhiệm vụ của bạn là khắc phục sự cố này bằng cách tái cấu trúc. Tái cấu trúc là quá trình cải thiện mã của bạn mà không thay đổi chức năng của mã. Ví dụ: đơn giản hoá logic hoặc di chuyển mã lặp lại vào các hàm riêng biệt.

Bạn có thể dùng loại lớp nào để mô hình hoá một nhóm giá trị riêng biệt có giới hạn nhằm khắc phục vấn đề này?

Nhóm của bạn muốn bạn thay đổi mã thời gian trong ngày để sử dụng một lớp enum. Bằng cách sử dụng lớp enum, các thành viên trong nhóm của bạn buộc phải chọn một trong các giá trị thời gian trong ngày được cung cấp, điều này ngăn chặn các loại vấn đề này.

Lớp enum phải được đặt tên là Daypart. Lớp này phải có 3 giá trị:

  • MORNING
  • AFTERNOON
  • EVENING

Bạn sẽ tạo lớp enum này như thế nào?

Bạn sẽ tái cấu trúc lớp Event của mình như thế nào để sử dụng lớp đó?

Thử viết mã giải pháp của bạn ngay bây giờ trước khi tiếp tục.

Nhấp vào Next (Tiếp theo) để xem cách chúng tôi mã hoá mã này.

6. Giải pháp cho Nhiệm vụ 2

enum class Daypart {
    MORNING,
    AFTERNOON,
    EVENING,
}

Lớp dữ liệu Event được tái cấu trúc hiện sử dụng lớp enum:

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

7. Nhiệm vụ 3

Các đồng nghiệp của bạn thích sử dụng Daypart đã được tái cấu trúc, nhưng họ có vấn đề khác.

Mã sau đây là cách họ đang tạo và lưu trữ các sự kiện của người dùng.

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)

Họ đã tạo nhiều sự kiện và mỗi sự kiện hiện yêu cầu một biến riêng. Càng tạo nhiều sự kiện thì việc theo dõi tất cả các sự kiện trở nên khó khăn hơn. Khi sử dụng phương pháp này, việc xác định số lượng sự kiện mà người dùng đã lên lịch sẽ khó đến mức nào?

Bạn có nghĩ ra cách tốt hơn để tổ chức việc lưu trữ những sự kiện này không?

Làm cách nào để lưu trữ tất cả sự kiện trong một biến? (Lưu ý: Tính năng này phải linh hoạt vì có thể sẽ có thêm nhiều sự kiện khác. Hàm này cũng cần phải trả về một cách hiệu quả số lượng sự kiện được lưu trữ trong biến.)

Bạn sẽ dùng loại hoặc loại dữ liệu nào? Bạn có thể thêm những sự kiện khác bằng cách nào?

Giờ đến lượt bạn triển khai tính năng này. Hãy thử viết mã trước khi nhấp vào Next (Tiếp theo) để xem giải pháp của chúng tôi.

8. Giải pháp nhiệm vụ 3

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

9. Nhiệm vụ 4

Người quản lý của bạn thích cách dùng ứng dụng, nhưng quyết định việc người dùng có thể xem bản tóm tắt về các sự kiện ngắn của họ, dựa trên thời lượng của sự kiện. Ví dụ: "Bạn có 5 sự kiện ngắn".

Sự kiện "ngắn" là một sự kiện có thời lượng dưới 60 phút.

Sử dụng mã biến events từ giải pháp của nhiệm vụ trước, bạn sẽ đạt được kết quả này như thế nào?

Nhấp vào Tiếp theo để tiếp tục xem giải pháp của chúng tôi.

10. Giải pháp cho Nhiệm vụ 4

Có nhiều cách để thực hiện việc này và sau đây là những gì chúng tôi đã quyết định:

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

11. Nhiệm vụ 5

Các thành viên trong nhóm của bạn thích cách thức hoạt động của ứng dụng nhưng họ muốn người dùng có thể xem bản tóm tắt tất cả sự kiện và thời gian trong ngày của họ.

Nội dung xuất sẽ tương tự như:

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

Sử dụng mã biến events từ bước trước, làm cách nào để bạn có thể đạt được kết quả này?

Nhấp vào Tiếp theo để xem mã giải pháp.

12. Giải pháp cho Nhiệm vụ 5

Sau đây là giải pháp của chúng tôi, nhưng các biến thể khác vẫn được chấp nhận.

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

13. Nhiệm vụ 6

Hiện tại, đồng nghiệp tìm và in mục cuối cùng bằng chỉ mục. Mã được sử dụng là: println("Last event of the day: ${events[events.size - 1].title}").

Người quản lý của bạn đề xuất kiểm tra tài liệu Kotlin để tìm ra hàm có thể đơn giản hoá mã này.

Bạn tìm thấy chức năng gì?

Hãy thử dùng hàm này để xác nhận rằng bạn nhận được cùng kết quả để in.

Nhấp vào Tiếp theo để xem giải pháp.

14. Giải pháp nhiệm vụ 6

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

15. Nhiệm vụ 7

Nhóm của bạn thích lớp dữ liệu mà bạn thiết kế, nhưng thấy rằng việc viết mã mỗi lần họ cần thời lượng của một sự kiện là một chuỗi lặp lại:

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

Bạn có thể sửa lỗi lặp lại này bằng cách trực tiếp thêm một phương thức vào lớp, nhưng cách thức này không lý tưởng vì các nhóm khác đã bắt đầu sử dụng lớp sự kiện của bạn trong ứng dụng của họ. Nếu lớp thay đổi, họ sẽ cần kiểm tra lại toàn bộ mã để đảm bảo không có gì xảy ra do sự thay đổi của bạn.

Khi không trực tiếp thay đổi lớp dữ liệu, làm cách nào để viết một thuộc tính mở rộng trả về các giá trị giống như mã ở trên?

Nếu triển khai chính xác, bạn sẽ có thể sử dụng mã sau và mã này sẽ in cùng một thông báo với mã xuất hiện ở đầu việc cần làm này.

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

Nhấp vào Next (Tiếp theo) để tiếp tục giải pháp.

16. Giải pháp nhiệm vụ 7

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

17. Thực hành bổ sung

Để thực hành thêm về ngôn ngữ Kotlin, hãy xem kênh Kotlin Core của JetBrains Academy. Để tìm chính xác một chủ đề cụ thể, hãy chuyển đến bản đồ tri thức để xem danh sách các chủ đề được đề cập trong kênh.