Re-architect - Domain Layer (一)

上一天我们提到了 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 之类的),又会觉得没有这个必要,所以我觉得目前这样放是一个比较好的选项。

还有 CoEditorContextMenu这两个元件之间的关系是组合,CoEditor 拥有 ContextMenu ,但是 ContextMenu 并不是永远都是处於可见的状态,所以我另外新增了两个状态:showContextMenu, showAdderButton。

class CoEditor {
    val showContextMenu: Observable<Boolean> 
    val showAdderButton: Observable<Boolean> 
    ...
    val contextMenu = ContextMenu()
}

最後是 CoEditorNoteRepository 之间的关系,理所当然的,也是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 被好好的回收掉。


<<:  Day16 Android - 不同fragment切换(ButtomNavigationView)

>>:  Day 16:Next 布景客制化 - 让副标题显示於标题内

[Day22] 在 GCP 的 VM 上安装 MySQL

MySQL or MariaDB CentOS 的 rpm 套件管理器预设没有 MySQL repo...

Material UI in React [ Day 16 ] Navigation Menu (下拉框)

Menu 这个套件应用的范围很广,之前讲解过的 Select 也是用这里的 MenuItem 来替换...

Day1 所以,网路到底是什麽?

在这个人手一机的时代,没接触过网路的人应该不多吧,不管是购物、查资料、追剧都会用到网路,但这个每天都...

[序章] 自然语言处理初探

前言 生活在网际网路以及智慧型手机普及的今天,与外国朋友聊天、出国旅行、与国外客户开商务会议,纵使不...

Day 7 拖动上传图片辨识数字

今天要做的是... 做一个前端网页,支援拖动图片上传, 把图片转成 base64 送给服务器,服务器...