簡體   English   中英

設計可觀察對象

[英]Designing observable objects

前言:這是一個關於反應式編程的設計問題。 它的目的是與語言無關,所以它都是偽代碼。 我懷疑無論正確答案是什么,都將同樣適用於 Rx/ReactiveCocoa/Combine。

我看到了 3 種不同的方式可以設計一個 object 來觀察。 每個人肯定都有優點/缺點,但並不完全清楚它們是什么。

  1. 您的 object 可能具有didChange: Publisher<()>屬性。 當您訂閱時,您會收到有關您發布的 object 何時更改的通知,但您實際上並沒有了解有關這些更改或 model ZA8CFDE6331BD59EB2AC96F8911C4B666 的新值的任何信息。 這很簡單,可以用map修復:

     class Point: BindableObject { var x, y: Int var didChange: Publisher<()> {... } } let object: Point =... let streamOfPoints = object.didChange // Publisher<()>.map { _ in object } // Publisher<Point>

    您只需通過其他參考直接訪問 object 即可為自己獲取 object 的值,此時您會收到更改通知。 如果您需要訪問xy值的 stream,這些也只是一個map調用。

    但是,這似乎有一些問題。

    1. 這是一個額外的步驟
    2. 它要求您可以訪問原始 object,因此傳遞發布者是不夠的。 您必須傳遞(Point, Publisher<Point>)對,這看起來很麻煩。
    3. 它可能存在正確性問題。 例如,從觸發didChange事件到您訪問 object 之間的任何延遲,您是否可能會讀取比觸發更改時的值更新的 object 值?

      這種方法是我最不喜歡的,但有趣的是,這是 Apple 在其 Combine 框架中采用的方法,帶有BindableObject協議。 我懷疑它可能與某種性能提升有關,在可能不需要的情況下,不必在 object 周圍使用 pipe。 是這樣嗎?

  2. 最明顯的方法是 ZF7B44CFFAFD5C52223D5498196C8A2E7BZ object 本身。 var didChange: Publisher<Point> { /* a publisher that emits self over time */ } . 這似乎實現了與 apporach #1 相同的效果,同時解決了我列出的 3 個問題。 我沒有看到方法#1 提供的任何價值。

  3. 您可以為 object 的每個字段創建發布者:

     class Point: BindableObject { let x = PublishSubject<Int> let y = PublishSubject<Int> }

    這更加細化,因此人們可以只訂閱他們關心的那些領域。 我不知道重量級訂閱有多重要,但是通過更具體地只訂閱您關心的內容可能會獲得一些性能優勢。 這是一個人為的例子,因為很難想到只需要知道一個點的 x 值或 y 值的情況。 但該原則仍然普遍適用。

    通過將 stream 映射到 xs 並進行重復數據刪除 ( .map { $0.x }.distinct ),也可以使用前兩種方法之一訪問 xs 和 ys。 但這比像這樣的直接訂閱調用了更多的映射閉包,這可能會對性能產生影響。

    這種方法也可以與方法 1 或 2 結合使用,以添加Publisher<()>Publisher<Point>類型的var didChange屬性,以便在需要觀察整個點 object 時使用。

    這曾經導致很多 API 膨脹。 在 Rx 中,要么:

    1. 要么你可以有x: Value<Int> ,並在整個地方使用x.value來訪問當前值
    2. 或者,你有var xObservable: Value<Int>var x: Int { xObservable.value } ,但這增加了很多 API 膨脹。

      幸運的是,屬性包裝器解決了這兩個問題,基本上實現了后一種設計,但無需顯式添加所有計算屬性(它們是為您生成的)。

您能否就使用哪種模式提供一些指導? 我懷疑這是“經驗顯而易見”的事情之一,但在反應式編程方面我還沒有做到。 謝謝!

這個問題有很多內容,但我可能會用選項 2 對 go 說。

我不是設計反應式框架的專家,但看看你的選擇,我們幾乎可以消除第一個因為太有限而無法使用,剩下的選項 2 和 3。

正如您所提到的,選項 3 允許很多粒度,但在我看來,這種粒度會導致常規用例更加臃腫 - 例如,我對已更改的整個 model 以及.map.filter我可以輕松地將選項 2 中的 stream 減少到需要時的細粒度選項。 在我看來,更常見的情況是查看正在觀察的整個 model,而不是其中的一部分。

目前,似乎選項 2 在 Rx 和類似框架的設計中很普遍,我認為這只是 go 關於它的經驗方式。

希望這會有所幫助,似乎這更適合討論板而不是簡單的答案。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM