mvmdemo
H5,iOS,Android,软件体系结构,MVM(中介,查看,ViewModel)
该设计最初是按照MVM的原理(中介,查看,ViewModel)进行概念化的。完成后,我进行了在线搜索以识别类似的方法。最终,Page Object Model, also known as POM, is a design pattern in Selenium that creates an object repository for storing all web elements.
但是,在iOS,Android和H5中,我尚未找到与此方面有关的设计模式。
I. MVM简介
在其核心上,它充当后端和UI之间的中介。它将请求发送到接口数据的后端,解析数据,并生成适合UI要求的数据模型,包括data
和function
组件。然后,前端直接呈现此模型,使UI开发人员摆脱了解决基础逻辑的需求。同时,逻辑开发人员摆脱了关注UI关注的关注。
start
在demo中,简单代码,初学者可以轻松理解
实施:https://github.com/AblerSong/MVMDemo
ii。设计方法
- 创建一个页面
(iOS: ViewController; Android: Activity or fragment; Vue:.vue)
- 将页面UI分为不同的组件,每个组件对应于
ViewModel
。 UI文本定义为ViewModel
中的变量,而UI点击事件定义为关闭。 - 建立一个
Mediator
,该Mediator
在完成API请求后,根据从后端返回的数据来初始化所有ViewModel
变量和封闭。有关特定详细信息,请参阅IV. Code Implementation。 - 将
Mediator
提供给UI开发人员,他们可以根据Mediator
中包含的数据直接绑定和渲染UI。
iii。特定的实施和详细要求(参考解决方案)
- #####遵循页面的部门。
- #####将页面分为基于“行”的不同组件,每个组件都对应于特定的
ViewModel
,所有组件都通过PageMediator
进行管理。 - #####使用每个
PageMediator
的MediatorManager Singleton
(在H5演示中,可以使用Vuex
)。这种方法可确保数据保持活力,而UI元素不能保持活力。
MediatorManager Singleton
管理PageMediators
,每个PageMediator
管理ViewModels
,如下所示:
MediatorManager.getSingleton().mediator = new Mediator
MediatorManager.getSingleton().mediator = null
如果页面包含许多组件,则
PageMediator
可能会变得复杂。在这种情况下,您可以使用适当的design patterns
来重构PageMediator
。分裂的特定方法取决于个人偏好和建筑能力。至关重要的是要仔细考虑
public
variables
和methods
在PageMediator
中。,只要UI的暴露API是合理的,随后的逻辑重构就不会影响UI,并且更改UI对UI的影响最少,对逻辑。-
在实践发展中,还应封装其他模块,以促进将来使用
Unit Test
作为UI Test
的替代。例如,路由器,吐司和网络等模块。例如,对于Toast
:
class ToastViewModel {
// use ReactiveX replace setter
set toast(value) {
Toast(value)
}
}
iv。代码实现
以下代码表明VUE使用bind,iOS使用tableView,而Android使用适配器进行渲染。数据绑定在每个页面的组件中执行,而所有逻辑都集中在中介器中。
查看
Mediator {
username_text = "username"
password_text = "password"
_username_str = ""
_password_str = ""
login_btn_disabled = true
constructor() {
this.init()
}
init() {}
set username_str(value) {
this._username_str = value
this.update_login_btn_disabled()
}
get username_str() {
return this._username_str
}
set password_str(value) {
this._password_str = value
this.update_login_btn_disabled()
}
get password_str() {
return this._password_str
}
update_login_btn_disabled() {
this.login_btn_disabled = !(this.username_str?.length && this.password_str?.length)
}
onSubmit() {
if (this.username_str == "admin" && this.password_str == "123456") {
router.back()
} else {
}
}
}
Android
class ButtonViewModel (
val buttonState: BehaviorSubject<Boolean> = BehaviorSubject.createDefault(false),
var buttonText: BehaviorSubject<String> = BehaviorSubject.createDefault(""),
) {
var clickItem = {}
}
class InputViewModel (
val text: BehaviorSubject<String> = BehaviorSubject.createDefault(""),
val value: BehaviorSubject<String> = BehaviorSubject.createDefault("")
) {}
class Mediator : BaseMediator () {
val usernameViewModel: InputViewModel = InputViewModel()
val passwordViewModel: InputViewModel = InputViewModel()
val buttonViewModel: ButtonViewModel = ButtonViewModel()
init {
usernameViewModel.text.onNext("username")
passwordViewModel.text.onNext("password")
val isNotEmpty: (String, String) -> Boolean = { name: String, age: String ->
name.isNotEmpty() && age.isNotEmpty()
}
val d1 = Observable.combineLatest(usernameViewModel.value, passwordViewModel.value, isNotEmpty).subscribe {
buttonViewModel.buttonState.onNext(it)
}
buttonViewModel.clickItem = {
val username = usernameViewModel.value.value
val password = passwordViewModel.value.value
if (username == "admin" && password == "123456") {
routerSubject.onNext(R.layout.activity_main)
} else {
ToastManager.toastSubject.onNext("input error")
}
}
compositeDisposable.add(d1)
}
val dataList by lazy { initList() }
private fun initList(): List<Map<String, Any>> {
val m1 = mapOf(Pair("viewType", R.layout.input_item), Pair("viewHolder", InputViewHolder::class.java), Pair("viewModel", usernameViewModel))
val m2 = mapOf(Pair("viewType", R.layout.input_item), Pair("viewHolder", InputViewHolder::class.java), Pair("viewModel", passwordViewModel))
val m3 = mapOf(Pair("viewType", R.layout.button_item), Pair("viewHolder", ButtonViewHolder::class.java), Pair("viewModel", buttonViewModel))
return listOf<Map<String, Any>>(m1, m2, m3)
}
}
ios
class ButtonCellViewModel: BaseViewModel {
let login_btn_disabled = BehaviorRelay(value: false)
var onSubmit = {}
}
class TextFieldCellViewModel: BaseViewModel {
let text = BehaviorRelay(value: "")
let value = BehaviorRelay(value: "")
}
class Mediator: BaseMediator {
let usernameViewModel = TextFieldCellViewModel()
let passwordViewModel = TextFieldCellViewModel()
let buttonCellViewModel = ButtonCellViewModel()
lazy var list: [[[String : Any]]] = {
let arr: [[[String : Any]]] = [
[
["model":usernameViewModel,"reuseIdentifier":textFieldCellReuseIdentifier],
["model":passwordViewModel,"reuseIdentifier":textFieldCellReuseIdentifier],
],
[
["model":buttonCellViewModel,"reuseIdentifier":buttonCellReuseIdentifier]
]
]
return arr
}()
override init() {
super.init()
initPasswordViewModel()
initUsernameViewModel()
initButtonCellViewModel()
}
func initUsernameViewModel() {
usernameViewModel.text.accept("username")
}
func initPasswordViewModel() {
passwordViewModel.text.accept("password")
}
func initButtonCellViewModel() {
let combineLatest = Observable.combineLatest(usernameViewModel.value, passwordViewModel.value)
combineLatest.map { (username: String, password: String) -> Bool in
return username.count > 0 && password.count > 0
}.bind(to: buttonCellViewModel.login_btn_disabled).disposed(by: disposeBag)
buttonCellViewModel.onSubmit = {
combineLatest.subscribe( onNext: { (username: String, password: String) in
if username == "Admin", password == "123456" {
RouterBehaviorSubject.onNext(RouterModel(type: .pop))
} else {
ToastBehaviorSubject.onNext("input error")
}
}).dispose()
}
}
}
V.优势和缺点
优点:
- 与Viper,MVI等这样的框架相比,核心想法更简单,更容易掌握。
- UI和逻辑的分离有助于任务分解和组合,增强代码可重复使用性。
- 正确分解了,
Mediator
可以代替UI test
; 这有助于直接白盒自动测试。 - 多亏了
MediatorManager Singleton
的存在,数据仍然存在,而UI则没有。数据是集中式的,可以轻松管理。 - 统一的业务代码结构使开发人员可以快速接管他人的代码。
缺点:
- 开发人员不注意注意力很容易导致内存泄漏,这可能是具有挑战性的。
vi。结论
从实际的发展角度来看,该框架非常适合H5应用。例如,在演示(VUE)中,将组件分解为.vue
,.scss
和.js
文件大大增强了代码可重复使用性,尤其是由于CSS文件的独立性。
对于H5,iOS和Android,使用Mediator
代替Unit test
代替UI test
可以显着降低错误并提高测试效率。
就个人而言,这种方法给我留下了深刻的印象。与单位测试的便利性相比