最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Kotlin設(shè)計(jì)模式之委托模式使用方法詳解

 更新時(shí)間:2023年09月06日 09:44:47   作者:謀愛(ài)先謀生愛(ài)人先愛(ài)己  
Kotlin提供了兩個(gè)本機(jī)功能來(lái)實(shí)現(xiàn)委托模式,第一個(gè)是接口委托(例如策略模式),另一種是屬性委托,它專(zhuān)注于類(lèi)成員/屬性(例如延遲加載、observable等),它們共同提供了一組豐富而簡(jiǎn)潔的功能,通過(guò)本博客,您將了解在什么情況下使用此模式

1、組合由于繼承

眾所周知,在面向?qū)ο笳Z(yǔ)言中,繼承是關(guān)鍵特性之一。這意味著您可以將一些功能和抽象放入基類(lèi)中。其他類(lèi)可以從基類(lèi)繼承因此獲得繼承的功能。這稱(chēng)為“Is-a”關(guān)系。作為教科書(shū)的示例,我們可以想象Shape類(lèi)(基類(lèi))和rectangle、circle等為派生類(lèi)。

過(guò)去,這種建模方式被濫用,導(dǎo)致設(shè)計(jì)不佳。例如,長(zhǎng)繼承鏈用于對(duì)象添加增量功能。作為一個(gè)過(guò)于夸張的示例,請(qǐng)考慮一下類(lèi)設(shè)置。我們想要?jiǎng)?chuàng)建“Animal”并實(shí)現(xiàn)它們的移動(dòng)和進(jìn)食方式。一種簡(jiǎn)單的方法是如下圖所示實(shí)現(xiàn)它。很明顯,這不會(huì)擴(kuò)展。

另一方面,下圖更傾向于組合而不是繼承。該類(lèi)Animal實(shí)現(xiàn)接口IMovingIEating。然而它沒(méi)有實(shí)際的實(shí)現(xiàn)代碼,它將調(diào)用委托給Walking和具體實(shí)現(xiàn)MeatEating。正如我們所看到的,這種設(shè)計(jì)更加靈活,因?yàn)榭梢詫?shí)現(xiàn)MovingEating而不用修改Animal類(lèi)。

委派繼承是實(shí)現(xiàn)SOLID設(shè)計(jì)模式中的開(kāi)放-關(guān)閉原則的一種方式。

2、接口委托

如何在Kotlin中創(chuàng)建委托? Kotlin提供了原生功能來(lái)實(shí)現(xiàn)委托模式,而無(wú)需編寫(xiě)任何樣板代碼。這僅適用于接口(即抽象類(lèi))。接口實(shí)現(xiàn)IWalking如下代碼所示。

// 標(biāo)準(zhǔn)委托
interface IMoving {
    fun move()
}
class Walking: IMoving {
    override fun move() {
        println("Walking")
    }
}
class Animal(private val movable: IMoving): IMoving {
    override fun move() {
        movable.move()
    }
}
fim main() {
    var movable = Walking()
    var animal = Animal(movable)
    animal.move()
}
// 內(nèi)置委托
interface IMoving {
    fun move()
}
class Walking: IMoving {
    override fun move() {
        println("Walking")
    }
}
class Animal(movable: IMoving): IMoving by movable {
}
fun main() {
    var walking = Walking()
    var animal = Animal(walking)
    animal.move()
}

正如我們所看到的,第一部分代碼需要實(shí)現(xiàn)接口方法。在正確的版本中,編譯器會(huì)為您執(zhí)行此操作。當(dāng)界面具有多種功能時(shí),效果會(huì)更加顯著。在派生類(lèi)類(lèi)型的超類(lèi)型的列表中使用by-子句表示可移動(dòng)的信息將在IMoving對(duì)象的內(nèi)部存儲(chǔ),編譯器將生成所有將轉(zhuǎn)發(fā)到IMoving的接口方法。

2.1、覆蓋接口成員

如果需要重寫(xiě)接口的特定成員函數(shù),只需在派生類(lèi)中編寫(xiě)該函數(shù)并添加關(guān)鍵字override即可。編譯器將使用重寫(xiě)方法的新規(guī)范。在下面的實(shí)例中,我們創(chuàng)建了該方法的新版本move

// By Delegate的重寫(xiě)功能
class Animal(movable: IMoving): IMoving by movable {
    override fun move() {
        println("Something else")
    }
}
fun main() {
    var walking = Walking()
    var animal = Animal(walking)
    animal.move()
}

2.2、多個(gè)接口/繼承

為了完成上面的例子,我們必須實(shí)現(xiàn)所有接口并將所有函數(shù)調(diào)用委托給成員變量。我們可以通過(guò)將與上一節(jié)相同的方法應(yīng)用于所有繼承的接口來(lái)做到這一點(diǎn)。

// 介個(gè)接口委托
interface IMoving {
    fun move()
}
class Walking: IMoving {
    override fun move() {
        println("Walking")
    }
}
interface IEating {
    fun eat()
}
class MeatEater: IEating {
    override fun eat() {
        println("Eat meat")
    }
}
class Animal(movable: IMoving, eating: IEating): IMoving by movable, IEading by eating {
}
fun main() {
    var walking = Walking()
    var eating = MeatEater()
    var animal = Animal(walking, eating)
    animal.move()
    animal.eat()
}

2.3、相同的函數(shù)簽名

如果您需要實(shí)現(xiàn)多個(gè)聲明了相同方法的接口,則會(huì)出現(xiàn)特殊情況。在這種情況下,委托是不明確的,編譯器會(huì)給出一個(gè)錯(cuò)誤,例如“Class 'xxx' must override public open fun doSomething(): Unit defined in Animal because it inherits many implementation of it”。您需要顯示實(shí)現(xiàn)(或重寫(xiě))此函數(shù)并手動(dòng)委托它。

2.4、在運(yùn)行時(shí)替換委托

通常需要在運(yùn)行時(shí)更改委托。這通常用在策略或狀態(tài)模式中。(目前)Kotlin不支持在使用by關(guān)鍵字進(jìn)行注入時(shí)更改委托。

以下代碼具有誤導(dǎo)性,因?yàn)樗蛴∫韵聝?nèi)容:

Walking
Walking
Running@xxx
//在運(yùn)行時(shí)更改委托
interface IMoving {
    fun move()
}
class Running: IMoving {
    override fun move() {
        println("Running")
    }
}
class Walking: IMoving {
    override fun move() {
        println("Walking")
    }
}
class Animal(var movable: IMoving): IMoving by movable {
}
fun main() {
    var walking = Walking()
    var animal = Animal(walking)
    animal.move()
    var running = Running()
    animal.movable = running
    animal.move()
    println(animal.movable)
}

3、屬性委托

什么是屬性委托? Kotlin提供了一些很好的功能來(lái)說(shuō)實(shí)現(xiàn)委托屬性。它向類(lèi)屬性添加了一些常見(jiàn)功能。您可以在庫(kù)中創(chuàng)建一些屬性(例如Lazy)并使用此功能包裝您的類(lèi)成員。

要了解其工作原理,您需要了解每個(gè)成員變量在幕后提供getValuesetValue。屬性委托不需要實(shí)現(xiàn)接口,但需要為每種類(lèi)型提供這些函數(shù)。正如您所看到的,屬性委托是通過(guò)對(duì)象/函數(shù)。

在下面的示例,我們提供了一個(gè)委托,該委托在每次訪問(wèn)時(shí)都會(huì)打印消息。

3.1、只讀委托

任何屬性委托必須至少為所委托的值類(lèi)型實(shí)現(xiàn)以下函數(shù)。這type T取決于被包裝的值類(lèi)型。

// Read requited delegate function
operator fun getValue(example: Any, property: KProperty<<*>): T {
    return // value of T
}

作為示例,我們將使用與Kotlin參考頁(yè)面略有不同的代碼。我們的委托只能用于字符串類(lèi)型,并且僅返回空字符串。這段代碼毫無(wú)用處,但它顯示了只讀屬性的用法。

// Example Read only delegate
class Example {
    val p: String by Delegate()
}
class Delegate {
    operator fun getValue(example: Any, property: KProperty<*>): String {
        return ""
    }
}

3.2、讀/寫(xiě)委托

為了使用上述委托進(jìn)行讀寫(xiě)實(shí)現(xiàn),我們還必須實(shí)現(xiàn)以下函數(shù)。請(qǐng)注意,我們有一個(gè)字符串值類(lèi)型。因此我們寫(xiě)了s: String。您需要將其調(diào)整為您想要使用的類(lèi)型。

// Write required delegate function
operator fun setValue(example: Any, property: KProperty<*>, s: String) {
}

我們上面的例子現(xiàn)在可以實(shí)現(xiàn)所需的功能了。我們將引入一個(gè)類(lèi)變量cached來(lái)保存實(shí)際值。

// Example Read/Write delegate
class Example {
    var p: String by Delegate()
}
class Delegate {
    var cached = ""
    operator fun getValue(example: Any, property: KProperty<*>): String {
        return cached
    }
    operator fun setValue(example: Any, property: KProperty<*>, s: String) {
        cached = s
    }
}

3.3、實(shí)現(xiàn)接口作為擴(kuò)展

另一種方法是將getValue()setValue()函數(shù)實(shí)現(xiàn)為類(lèi)的擴(kuò)展函數(shù)Delegate。如果該類(lèi)不在您的源代碼管理中,這很有用。

// Delegated property as extension function
class Example {
    val string: String by Delegate()
}
class Delegate {
}
operator fun Delegate.getValue(example: Any, propety: KProperty<*>): String {
    return ""
}

3.4、通用委托定義

明顯的缺點(diǎn)是此代碼僅適用于字符串類(lèi)型的對(duì)象。如果我們想將它用于其他類(lèi)型,我們必須使用泛型類(lèi)。我們將向您展示如何更改上述示例以使其兼容所有類(lèi)型。

// Generic class for Delegate
class Example {
    var string: String by Delegate("hello")
    var int: Int by Delegate(3)
}
class Delegate<T>(var cached: T) {
    operator fun getValue(example: Any, property: KProperty<*>): T {
        return cached
    }
    operator fun setValue(example: Any, property: KProperty<*>, s: T) {
        cached = s
    }
} 

3.5、匿名對(duì)象委托

Kotlin還提供了一種創(chuàng)建匿名對(duì)象委托而無(wú)需創(chuàng)建新類(lèi)的方法。這是由于接口ReadOnlyPropertyReadWriteProperty標(biāo)準(zhǔn)庫(kù)而起作用的。這些接口將為ReadOnlyProperty提供getValue函數(shù)。ReadWriteProperty通過(guò)添加setValue()函數(shù),擴(kuò)展了ReadOnlyProperty。

在上面的示例,委托屬性是通過(guò)函數(shù)調(diào)用創(chuàng)建為匿名對(duì)象的。

// Anonymous objects delegates
class Example {
    var string: String by delegate()
}
fun delegate(): ReadWriteProperty<Any?, String> = object: ReadWriteProperty<any?, String> {
    var curValue = ""
    override fun getValue<thisRef: Any?, property: KProperty<*>): String = curValue
    override fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
        curValue = value
    }
}

3.6、委托給另一個(gè)屬性

從一個(gè)屬性委托給另一個(gè)屬性可能是提供向后兼容性的一個(gè)非常有用的技巧。想象一下,在類(lèi)的1.0版本中,您有一些公共成員函數(shù)。但在他們的一生中,名字發(fā)生了變化。為了不破壞客戶端,我們可以將舊的成員函數(shù)標(biāo)記為deprecated(通過(guò)注解)并將其調(diào)用轉(zhuǎn)發(fā)到新的實(shí)現(xiàn)。

// Delegate to other member
class Example {
    var newName: String = ""
    @Deprecated("Use 'newName' instead", ReplaceWith("newName"))
    var oldName: String by this::newName
}
fun main() {
    val example = Example()
    example.oldName = "hello"
    println(example.newName)
}

我們的經(jīng)驗(yàn),在某些情況下,此代碼無(wú)法編譯,并出現(xiàn)錯(cuò)誤代碼:

知道有一個(gè)開(kāi)放的錯(cuò)誤(LINK),我們希望它能盡快修復(fù)!

4、委托示例和用例

Kotlin為常見(jiàn)問(wèn)題提供了一些預(yù)先實(shí)現(xiàn)的解決方案。請(qǐng)?jiān)L問(wèn)KotlinLang頁(yè)面查看完整列表。對(duì)于當(dāng)前的實(shí)現(xiàn),我們將向您展示幾個(gè)示例。

4.1、通過(guò)observable

可以通過(guò)使用可觀察委托來(lái)獲得基本功能。當(dāng)值改變時(shí)它提供回調(diào)。這樣做得好處是您可以訪問(wèn)新的和以前的值。要使用可觀察委托,您必須指定初始值。

// by observable
class Book {
    var content: Strign by obserable("") { property, oldValue, newValue ->
        println("Book changed")
    }
}
fun main() {
    val book = Book()
    book.content = "New content"
}

4.2、通過(guò)vetoable

vetoable有點(diǎn)類(lèi)似觀察者委托。然而,回調(diào)函數(shù)可用于撤銷(xiāo)修改。請(qǐng)注意,回調(diào)必須返回布爾值。如果成功,則為true;如果失敗,則為false。在我們的書(shū)籍示例中,我們檢查書(shū)籍的信內(nèi)容是否為null或?yàn)榭铡T谶@種情況下,它被認(rèn)為是錯(cuò)誤的,我們想要vetoable該更改。

// vetoable
class Book {
    var content: String by vetoable("") { property, oldValue, newValue ->
        !newValue.isNullOrEmpty()
    }
}
fun main() {
    val book = Book()
    book.content = "New content"
    println(book.content)
    book.content = ""
    println(book.content)
}

4.3、通過(guò)Lazy

Kotlin提供了變量延遲計(jì)算的現(xiàn)有實(shí)現(xiàn)。一個(gè)常見(jiàn)的用例是需要花費(fèi)大量時(shí)間來(lái)初始化但在開(kāi)始時(shí)不會(huì)直接使用的成員變臉。例如,考慮一個(gè)Book類(lèi),它獲取一個(gè)表示整個(gè)文本的字符串。書(shū)籍類(lèi)擁有另一個(gè)對(duì)書(shū)籍進(jìn)行昂貴的后處理的類(lèi)。開(kāi)發(fā)人員決定讓這個(gè)示例變得Lazy,因?yàn)樗⒉皇强偙徽{(diào)用。

Lazy屬性附加到analyzer類(lèi)。運(yùn)行代碼時(shí),它首先會(huì)打印出書(shū)籍已創(chuàng)建,然后analyzer成員變量已實(shí)例化。

// by lazy
class Book(private val rawText: String) {
    private val analyser: Analyser by lazy { Analyser() }
    fun analyse() {
        analyser.doSomething()
    }
}
class Analyser {
    init {
        println("Init analyser class")
    }
    fun doSomething() {
        print("DoSomething")
    }
}
fun main() {
    val rawText = "MyBook"
    val book = Book(rawText)
    println("Book is created")
    book.analyse()
}

4.4、通過(guò)Not Null

返回具有非null值的讀/寫(xiě)屬性的屬性委托,該屬性不是在對(duì)象構(gòu)造期間而是在稍后的時(shí)間初始化。由于它是在稍后時(shí)間點(diǎn)創(chuàng)建的,因此與延遲初始化有關(guān)。然而,一個(gè)很大的缺點(diǎn)是沒(méi)有本地方法可以知道該值是否已初始化。它需要一些樣板代碼才能保存。我們的十本里將類(lèi)似于以下代碼。請(qǐng)注意,Lazy要好得多。

// By Delegates.NotNull
class Analyser {
    init {
        println("Init analyser class")
    }
    fun doSomething() {
        println("DoSomething")
    }
}
class Book(private val rawText: String) {
    var analyser: Analyser by Delegates.notNull()
    fun analyse() {
        analyser = Analyser()
        analyser.doSomething()
    }
}
fun main() {
    val rawText = "MyBook"
    val book = Book(rawText)
    println("Book is created")
    book.analyse()
}

4.5、Logging

您可以使用委托對(duì)應(yīng)用程序日志記錄庫(kù)的訪問(wèn)。在帶有惰性的函數(shù)中,這是為每個(gè)需要的類(lèi)提供對(duì)記錄器的訪問(wèn)的最慣用的方法。

// Idiomatic logging access
fun <R: Any> R.logger(): Lazy<Logger> {
    return lazy {
        Logger.getLogger(unwrapCompainionClass(this.javaClass).name)
    }
}
class Something {
    val LOG by logger()
    fun foo() {
        LOG.info("Hello from Something")
    }
}

以上就是Kotlin設(shè)計(jì)模式之委托模式使用方法詳解的詳細(xì)內(nèi)容,更多關(guān)于Kotlin委托模式使用的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Flutter打包APK的幾種方式小結(jié)

    Flutter打包APK的幾種方式小結(jié)

    Flutter打包不同于RN,Flutter可以在Android?Studio里編寫(xiě)Flutter代碼并最終打包為APK,本篇主要闡述涉及到的幾種打包方式,通過(guò)圖文結(jié)合的方式介紹的非常詳細(xì),需要的朋友可以參考下
    2025-03-03
  • Android Gradle Build Error:Some file crunching failed, see logs for details的快速解決方法

    Android Gradle Build Error:Some file crunching failed, see l

    這篇文章主要介紹了Android Gradle Build Error:Some file crunching failed, see logs for details的快速解決方法的相關(guān)資料,需要的朋友可以參考下
    2016-10-10
  • 基于Android"今日事今日畢"的使用介紹

    基于Android"今日事今日畢"的使用介紹

    本篇文章是對(duì)Android"今日事今日畢"的使用進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下
    2013-05-05
  • Android實(shí)現(xiàn)伸縮彈力分布菜單效果的示例

    Android實(shí)現(xiàn)伸縮彈力分布菜單效果的示例

    本文介紹下在Android中實(shí)現(xiàn)伸縮彈力分布菜單效果。這種效果比較炫酷,有需要的朋友可以參考一下。
    2016-10-10
  • Android共享元素動(dòng)畫(huà)效果顯示問(wèn)題解決

    Android共享元素動(dòng)畫(huà)效果顯示問(wèn)題解決

    什么是共享元素呢?可以理解為當(dāng)頁(yè)面跳轉(zhuǎn)是,看起來(lái)一個(gè)View屬于界面A又屬于界面B,下面這篇文章主要給大家介紹了關(guān)于Android共享元素動(dòng)畫(huà)效果顯示問(wèn)題的相關(guān)資料,需要的朋友可以參考下
    2022-02-02
  • Android自定義Banner輪播效果

    Android自定義Banner輪播效果

    這篇文章主要為大家詳細(xì)介紹了Android自定義Banner輪播效果,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-04-04
  • Flutter實(shí)現(xiàn)滾動(dòng)選擇數(shù)字

    Flutter實(shí)現(xiàn)滾動(dòng)選擇數(shù)字

    這篇文章主要為大家詳細(xì)介紹了Flutter實(shí)現(xiàn)滾動(dòng)選擇數(shù)字,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-03-03
  • Android實(shí)現(xiàn)底部導(dǎo)航欄效果

    Android實(shí)現(xiàn)底部導(dǎo)航欄效果

    這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)底部導(dǎo)航欄效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-01-01
  • Android用文件存儲(chǔ)數(shù)據(jù)的方法

    Android用文件存儲(chǔ)數(shù)據(jù)的方法

    這篇文章主要為大家詳細(xì)介紹了Android用文件存儲(chǔ)數(shù)據(jù)的方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-10-10
  • Android中invalidate()和postInvalidate() 的區(qū)別及使用方法

    Android中invalidate()和postInvalidate() 的區(qū)別及使用方法

    Android中實(shí)現(xiàn)view的更新有兩組方法,一組是invalidate,另一組是postInvalidate,其中前者是在UI線程自身中使用,而后者在非UI線程中使用。本文給大家介紹Android中invalidate()和postInvalidate() 的區(qū)別及使用方法,感興趣的朋友一起學(xué)習(xí)吧
    2016-05-05

最新評(píng)論

临猗县| 弋阳县| 海林市| 清丰县| 和平县| 博兴县| 阿坝| 溧阳市| 江都市| 伊川县| 海城市| 滦平县| 监利县| 周口市| 大新县| 彰武县| 固安县| 巴青县| 黑水县| 襄汾县| 漳平市| 色达县| 泰来县| 自贡市| 时尚| 秦皇岛市| 利津县| 汉中市| 五大连池市| 阿巴嘎旗| 故城县| 灌南县| 安陆市| 格尔木市| 盈江县| 余江县| 拉萨市| 图木舒克市| 肇庆市| 台江县| 措勤县|