上一天我們提到了 Domain Layer 會包含以下三個組件:CoEditor
, ContextMenu
, NoteRepository
,今天我們將著重在介面設計。
這個“共編器”將會提供所有的核心操作邏輯,基本的公開函式與變數如下:
class CoEditor {
val allVisibleNotes: Observable<List<String>>
val selectedNote: Observable<Optional<Note>>
fun selectNote(noteId: String)
fun clearSelection()
fun addNewNote()
fun moveNote(noteId: String, positionDelta: Position)
}
我們可以發現其實很多函式的介面跟之前的 EditorViewModel
一模一樣,因為 EditorViewModel
在原本的架構中就是屬於 Domain 層的元件,這樣的結果一點都不奇怪。至於其他原本 EditorViewModel
有的公開函式但是必較偏向於選單操作的,我們將它搬移到了 ContextMenu
,介面如下:
class ContextMenu {
val colorOptions: List<YBColor>
val selectedColor: Observable<YBColor>
fun onColorSelected(color: YBColor)
fun onDeleteClicked()
fun onEditTextClicked()
}
如此一來我們就把原本屬於 EditorViewModel
的職責一分為二了!onColorSelected
, onDeleteClicked
, onEditTextClicked
這些函式放在 ContextMenu
中也讓第一眼看到這個類別的人,完全知道這類別可以做哪些事情,同時還有公開變數表示了所有顏色的選項 colorOptions
以及現在正在選的顏色 selectedColor
,這兩個變數是給顏色選單顯示用的變數,其實這樣設計並沒有很 “Domain”,這些是比較零散的知識,但是如果要為它再設計另一個階層(像是 ColorMenu 之類的),又會覺得沒有這個必要,所以我覺得目前這樣放是一個比較好的選項。
還有 CoEditor
與 ContextMenu
這兩個元件之間的關係是組合,CoEditor
擁有 ContextMenu
,但是 ContextMenu
並不是永遠都是處於可見的狀態,所以我另外新增了兩個狀態:showContextMenu, showAdderButton。
class CoEditor {
val showContextMenu: Observable<Boolean>
val showAdderButton: Observable<Boolean>
...
val contextMenu = ContextMenu()
}
最後是 CoEditor
與 NoteRepository
之間的關係,理所當然的,也是CoEditor
擁有 NoteRepository
,不然 CoEditor
將無法拿到所有便利貼的狀態:
class CoEditor(private val noteRepository: NoteRepository)
NoteRepository
的實作將會是 Firebase 並且由 Dependency Injection framework 來提供。CoEditor
最後完整的公開函式會是像這樣:
class CoEditor(private val noteRepository: NoteRepository) {
val allVisibleNotes: Observable<List<String>>
val selectedNote: Observable<Optional<Note>>
val showContextMenu: Observable<Boolean>
val showAdderButton: Observable<Boolean>
val openEditTextScreen: Observable<String> // 打開編輯文字頁面,上面沒提到
val contextMenu = ContextMenu()
fun selectNote(noteId: String)
fun clearSelection()
fun addNewNote()
fun moveNote(noteId: String, positionDelta: Position)
}
class ContextMenu {
val colorOptions: List<YBColor>
val selectedColor: Observable<YBColor>
fun onColorSelected(color: YBColor)
fun onDeleteClicked()
fun onEditTextClicked()
}
但這邊還有一個問題,RxJava 一但綁定了,就必須要處理他的生命週期,CoEditor
一定也免不了這件事情發生,這也代表了這個類別中會有一些非同步的任務,所有的非同步任務都要好好的被處理,不然會有 memory leak 的問題發生,於是這個 CoEditor
也會是一個有生命週期的元件,其生命週期的事件將由外界控制:
class CoEditor(private val noteRepository: NoteRepository) {
....
private val disposableBag = CompositeDisposable()
fun start() { ... }
fun stop() { disposableBag.clear() }
}
通常來說一個具有生命週期的元件都是要有成對的生命週期事件,有死亡(stop)就要有出生(start),所有在 start
中被觀察的 Observable 要在 stop
被好好的回收掉。