原文来自 被滥用的 GUI 设计模式。编程
随便侃些我的对 GUI 设计模式的见解。设计模式
近些年来,随着 Fronted 技术的火热和推动,古老的(至少有几十年历史)用来解决 GUI 应用中代码组织问题的「GUI 设计模式」如今也成为了 Frontend 工程师的热门话题,MVC、MVP、MVVN 等设计模式在网路上被议论不绝。有不少工程师开始经过写博文来介绍它们、阐述本身对它们的理解,甚至在 Github 上开源了各类 GUI 设计模式的实现。网络
顺着这种趋势,不少 Frontend 工程师甚至把 GUI 设计模式当成一种「规范」乃至「教条」。然而糟糕的现实是,大多数人并无正确地、细致地理解和运用 GUI 设计模式,反而由于 Tradeoff 致使它的缺点被放大。结果就是你用了大量精力、模板代码去设计它,反而让它更复杂、更难维护了。函数式编程
例如,当你打开 Github 上大多数试图实现 GUI 模式的仓库时会发现,整个应用大概也就两三个页面、四五个网络接口,就可能已经建立了几十个类和接口来承载那单薄的逻辑了。举个更具体的例子,我我的曾经接触过几个用 MVP 模式设计的大型 Android 工程,在进行维护或者迭代的时候,各类带有问题的设计反而让 MVP 模式成为了累赘。函数
首先,工程中大多数 View 都是粒度大耦合度高的 Activity 类,并且不少 View 里为了方便,会提供 fun updateView(user: UserModel)
这样的方法,致使 View 和领域/业务模型直接耦合了。再者,View 和 Model 中还会包含了跳转页面、发送全局消息等各类带有「反作用」的命令,这也让面向接口编程成为了形式主义。工具
因此与其「舍本逐末」、「知其然而不知其因此然」,倒还不如理解问题的本质。于 GUI 设计模式而言,实际上最重要的思想是「分而治之」,经过把以前都写在一处的代码按照职能分到不一样的类,来让它们实现「低耦合高内聚」。因此,咱们更应该把 GUI 设计模式当成一思想而不是具体的手段,更也不必用各类所谓的模板来解决问题,只要你能把热点、关键代码设计得足够低耦合高内聚,那么你彻底能够无视全部 GUI 设计模式。post
例如上面提到的 fun updateView(user: UserModel)
问题,实际有两种方式来让 View 和业务模型 UserModel 解耦:学习
// 方法一
interface ViewA {
fun updateText1(text: String)
fun updateText2(text: String)
// ...
}
class PresenterA {
fun onSomeEvent() {
val userModel = Apis.requestUser()
viewA.updateText1(userModel.name)
viewA.updateText2(userModel.age.toString())
}
}
// 方法二
interface ViewA {
data class ViewAttributes(
text1: String,
text2: String,
// ...
)
fun updateView(view: ViewAttributes)
}
class PresenterA {
fun onSomeEvent() {
val viewAttributes = Apis.requestUser().mapTo ViewAttributes()
viewA.updateView(viewAttributes)
}
}
复制代码
方法一更倾向于用「指令」来描述 View,方法二则更倾向于用「数据」。而我我的更喜欢方法二,由于数据是运行时可处理、可持久化的,甚至能够跨进程、跨语言、乃至跨机器共享的。讲个题外话,Web Fronted 里 Redux 等状态管理工具捧起了一个很火的词「时间旅行」。在我看来核心思想其实也是把指令下沉,用数据(/状态)来描述上层逻辑,这样就能够在运行时实现逻辑可记录、可回放。ui
这里还有一点须要注意的,ViewAttributes
必须是 View 的领域模型,字段名称应当仅和 View 自己相关,而不该该和其余领域有关系。spa
再回到以前提到过的另一个问题:View 和 Model 里的反作用。这个其实更容易解决,只须要把全部反作用移到外部(/调用方)就行了。例如:
// 有反作用
class ViewA {
fun onTitleClick() {
sendBroadcast("x")
}
}
// 无反作用
class ViewA {
fun onTitleClick() {
caller.onTitleClick()
}
}
class PresenterA {
fun onTitleClick() {
sendBroadcast("x")
}
}
复制代码
实际上,只懂得 OOP(面向对象编程)的工程师很容易形成前面提到的问题,由于他们习惯了依赖「外部状态」来解决问题(类的实例自己也是一个状态),可是在状态数量不断增长的状况下,状态的管理反而会成为一个新的大难题。而 OOP 提倡类的「低耦合高内聚」实际上能够当作是在解决状态管理的问题。
因此在文章的最后,我强烈推荐工程师们能够学习下 FP(函数式编程)。相对于 OOP 而言,FP 的思想则是摒弃外部状态,它实现的是粒度更小的函数级别的「低耦合高内聚」,你只须要保证你的函数是无反作用的而后管理好函数内部的状态就能够了。而维持这种编程思想,能让你轻松驾驭巨型、复杂的项目,甚至能让你的代码更容易被调试,更容易被并行执行。
对于 Android 工程师们来讲,Kotlin 目前的火热正是让你们有了更了解 FP 的机会。以后我也会写些和 Kotlin、FP 有关的文章。