iT邦幫忙

2023 iThome 鐵人賽

DAY 4
1
Kotlin

Kotlin魔法:Spring Boot 3的fp奇幻冒險系列 第 4

[小草原] Kotlin的劍 data class、Value class

  • 分享至 

  • xImage
  •  

前言

今天我們要接續昨天的份,來研究如何用kotlin寫出Product type以及Sum type

data class

data class Customer(
    val name: String,
    val email: String,
    val age: Int,
)

我們想要創造出一個顧客的Class,他有三個屬性,Name,email,age,但現在這樣的type,並不安全,我們也許會把email填到name裡面,也不會噴錯。因此我們可以再設計一下。

data class 及 value class

@JvmInline
value class Name(val value: String)

@JvmInline
value class Email(val value: String)

@JvmInline
value class Age(val value: Int)

data class Customer(
    val name: Name,
    val email: Email,
    val age: Age,
)

這邊我們使用value class來包裝已經存在的primitive type,而且value class的特性,不會造成額外的開銷,因為它在運行時會被消除。
這樣做的話,我們再創造customer type時,就不會發生放錯屬性的時候了!

val customer: Customer = Customer(Name("JW"), Email("myEmail"), Age(25))

如果有人不想填email

我們在設計時,可能email並不是一定要輸入的選項,那麼我們目前的Customer type可能就不能代表這件事情了,我們的domain type是可以表達事情的。因此我們可以這樣來改進,加上?

data class Customer(
    val name: Name,
    val email: Email?,
    val age: Age,
)

兩個就只能選一個,要怎麼表示?

假設我們的Customer要更進一步,新增電話號碼選項,並且phone跟email要擇一填寫,我們該怎麼做呢?難道要兩個都要可以給Null嗎?

兩個都給可為Null?

data class Customer(
    val name: Name,
    val email: Email?,
    val age: Age,
    val phone: Phone?,
)

這樣好像可以表達,如果email為空,phone填值就可以了,如果phone為空,email填值就可以了。
但這樣會有幾個缺點。

  • 語義不清晰: 這種方式對於代表 "email 為空,phone 有值" 和 "phone 為空,email 有值" 的情況沒有提供清晰的語義。這可能會導致在程式碼中的理解和維護時出現困難。
  • 邏輯錯誤: 我們有可能將兩者都填上數值,或是兩者皆為空,這樣就會出現錯誤。

Sealed Class

可以將Sealed Class當作Enum的進階版本,我們就可以這樣來改寫

sealed interface ContactInfo

@JvmInline
value class Email(val value: String) : ContactInfo

@JvmInline
value class Phone(val value: String) : ContactInfo

這樣我們就可以使用ContactInfo來表示可能是email或是phone的二擇一囉

完整code

package model

@JvmInline
value class Name(val value: String)

sealed interface ContactInfo

@JvmInline
value class Email(val value: String) : ContactInfo

@JvmInline
value class Phone(val value: String) : ContactInfo

@JvmInline
value class Age(val value: Int)

data class Customer(
    val name: Name,
    val contactInfo: ContactInfo,
    val age: Age,
)

val customer: Customer = Customer(Name("JW"), Phone("123"), Age(25))
val customer2: Customer = Customer(Name("JW"), Email("myEmail"), Age(25))


參考資料

https://arrow-kt.io/learn/design/domain-modeling/


上一篇
[新手村] Domain Modeling的魔力
下一篇
[小草原] Spring Boot 3的RESTful API
系列文
Kotlin魔法:Spring Boot 3的fp奇幻冒險30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言