在 Kotlin 中,delegation pattern 已經被內建為語言的一部分,所以開發時可以以更簡潔的方式實作委託模式。Kotlin 提供了 by 關鍵字,可以容易地將某個介面的實作委託給另一個對象。
以下是使用 Kotlin 委託模式的基本步驟和範例
interface SoundBehavior {
fun makeSound()
}
class CatSound : SoundBehavior {
override fun makeSound() {
println("Meow!")
}
}
class DogSound : SoundBehavior {
override fun makeSound() {
println("Woof!")
}
}
這裡,Animal 類別不直接實作 SoundBehavior 接口,而是將實作委託給另一個 SoundBehavior 物件。
class Animal(soundBehavior: SoundBehavior) : SoundBehavior by soundBehavior
如此一來,當呼叫 Animal 的 makeSound() 方法時,實際上是呼叫被委託物件的方法。
除了上述基本的委託外,Kotlin 還提供了一些內建的委託功能,例如 Lazy, Observable 等,這在前幾天的章節有提到。
在 Android 開發中,Kotlin 的委託模式也是非常實用的。以下是一些常見的使用情境:
在 Android 的 View Binding 與 Kotlin 合作時,可以使用委託模式來簡化程式碼。例如,如果你有一個名為 ActivityMainBinding 的綁定類別,你可以這樣使用:
class MainActivity : AppCompatActivity() {
private val binding by viewBinding(ActivityMainBinding::inflate)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(binding.root)
binding.textView.text = "Hello, Kotlin Delegates!"
}
}
viewBinding 是一個定義好的擴展屬性,它將 View Binding 的初始化與清理操作委託給了 ActivityMainBinding::inflate 方法。
使用委託來簡化 DataStore<Preferences>
的讀取和寫入操作。例如:
class UserPreferences(context: Context) {
private val dataStore: DataStore<Preferences> = context.createDataStore("user_prefs")
// 委託屬性
var usernameFlow: Flow<String?> = dataStore.data.map { it[USERNAME_KEY] }
var ageFlow: Flow<Int?> = dataStore.data.map { it[AGE_KEY] }
suspend fun saveUsername(username: String) {
dataStore.edit { preferences ->
preferences[USERNAME_KEY] = username
}
}
suspend fun saveAge(age: Int) {
dataStore.edit { preferences ->
preferences[AGE_KEY] = age
}
}
companion object {
val USERNAME_KEY = preferencesKey<String>("username")
val AGE_KEY = preferencesKey<Int>("age")
}
}
使用 Kotlin 的委託模式與 Android Architecture Components 一同工作時,可以大幅簡化程式碼。例如,使用委託模式簡化 LiveData 的觀察:
class MainActivity : AppCompatActivity() {
private val viewModel: MyViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
viewModel.myLiveData.observe(this) { data ->
// 更新 UI
}
}
}
使用 Navigation Component 的 Safe Args 插件時,也可以利用 Kotlin 的委託模式。例如:
class DetailFragment : Fragment(R.layout.fragment_detail) {
val args: DetailFragmentArgs by navArgs()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val itemId = args.itemId
//...
}
}
Dagger 或 Hilt 的注入 (Dependency Injection)
這些只是 Kotlin 委託模式在 Android 開發中應用的一些例子。實際上,隨著社群的發展,還有很多其他的工具和框架利用了 Kotlin 的這一特性,讓 Android 開發變得更加簡潔和高效。
Ktor 是一個 Kotlin 撰寫的輕量級後端框架。在 Ktor 中,features 可以被認為是一些增加功能的插件,而許多 features 都可以透過委託模式進行簡單地設定:
install(StatusPages) {
exception<Throwable> { cause ->
call.respond(HttpStatusCode.InternalServerError, cause.localizedMessage)
}
}
在上面的範例中,StatusPages 是一個 Ktor 的 feature,它允許我們定義如何處理特定的異常或錯誤狀況。
使用 Spring Boot 和 Kotlin 時,我們可以利用委託模式簡化某些設定或操作,例如應用設定的載入:
@ConfigurationProperties(prefix = "app")
class AppProperties {
var name: String by Delegates.notNull()
var version: String? = null
}
這裡,Delegates.notNull() 確保了 name 屬性在使用前一定被初始化,否則會拋出異常。
若使用像是 Exposed 這種 Kotlin 原生的資料庫框架,你可能會選擇封裝 DAO 層。這樣的情況下,委託模式能幫助你將具體的實作隱藏起來,僅暴露必要的接口:
class UserService(private val dao: UserDao) : UserDao by dao {
// 你可以在這裡加入其他的業務邏輯或方法
}
這裡,UserService 將 UserDao 的所有方法都委託給了 dao 物件,但同時你可以在 UserService 中加入其他的邏輯或方法。
在後端開發中,委託模式通常用於封裝或簡化常見的任務,並提供更大的彈性。藉由將特定的實作或邏輯委託給其他對象,可以使得系統更容易擴展和維護。
今日播放 TomBoy 的 stage mix