前言-- 应用架构
初次接触编程时相信就算是最天才的程序员也是把所有代码(甚至是几千行)写在一个文件里,但是慢慢的我们会发现其实这样是很不好的,一个月后或许就连自己看这些代码都会发现,卧槽这是写的啥。
只要是公司级别的项目,代码开发都是多人合作,虽然特定的模块常有特定的团队维护,但是不符合标准的代码也是不利于传承的,团队的人员会变迁,团队也会变迁。
应用架构是一个很重要的指标,良好的应用架构=更快定位错误代码 + 新人更好快速上手 + 更好的应用。
在Android,被人们说烂了的架构叫MVC,MVP,MVVC,但是我写下这篇文章的原因,是因为我发现我并不是完全、真正懂这三个架构。
正文-- 你真的懂它们吗
SRP原则-- 单一责任原则
MVC 梦开始的地方
最古老的架构就是MVC,Android系统从宏观来看也是MVC
Model: 数据模型与状态,包含应用的业务逻辑
View: 视图,把数据表现出来,即布局
Controller: 模型和视图之间的通道,负责反映用户的交互
- 用户通过 View进行交互。
- View 将用户的交互信息传递给 Controller。
- Controller 接收到信息后,调用 Model 更新数据或执行业务逻辑。
- Model 更新数据后,通知 Controller 数据已经改变。
- Controller 接收到 Model 的通知后,将更新的数据传递给 View,使界面显示最新的数据。
// Model
data class User(val firstName: String, val lastName: String)
// controller
class UserController(private val view: MainActivity) {
private var user: User? = null
fun onSaveButtonClicked(firstName: String, lastName: String) {
user = User(firstName, lastName)
user?.let {
view.displayUserInfo(it)
}
}
}
// view
class MainActivity : AppCompatActivity() {
private lateinit var controller: UserController
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
controller = UserController(this)
val firstNameInput: EditText = findViewById(R.id.firstNameInput)
val lastNameInput: EditText = findViewById(R.id.lastNameInput)
val saveButton: Button = findViewById(R.id.saveButton)
val displayTextView: TextView = findViewById(R.id.displayTextView)
saveButton.setOnClickListener {
val firstName = firstNameInput.text.toString()
val lastName = lastNameInput.text.toString()
controller.onSaveButtonClicked(firstName, lastName)
}
}
fun displayUserInfo(user: User) {
val displayTextView: TextView = findViewById(R.id.displayTextView)
displayTextView.text = "User: ${user.firstName} ${user.lastName}"
}
}
导致问题:
- view和controller是紧耦合的,难以单元测试和重构
- controller需要知道view的实现细节
- 三方职责不够明确
MVP 升级
MVP是MVC的升级版,提供了更可测试性,易维护,干净的编码方式。
Model:同MVC一直,数据模型和状态,包含应用业务逻辑
View:视图,布局,以及Activity和Fragment,当包含被Presenter操作的接口
Presenter:处理View的请求,从Model获取数据,并将数据返回给View进行显示
// model
data class User(val id: Int, val name: String)
interface UserRepository {
fun getUser(userId: Int): User
}
class UserRepositoryImpl : UserRepository {
override fun getUser(userId: Int): User {
return User(userId, "User$userId")
}
}
// view
interface UserView {
fun showUser(user: User)
fun showError(message: String)
}
class UserActivity : AppCompatActivity(), UserView {
private lateinit var presenter: UserPresenter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_user)
presenter = UserPresenterImpl(this, UserRepositoryImpl())
presenter.loadUser(1)
}
override fun showUser(user: User) {
findViewById<TextView>(R.id.userName).text = user.name
}
override fun showError(message: String) {
Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
}
}
// Presenter
interface UserPresenter {
fun loadUser(userId: Int)
}
class UserPresenterImpl(
private val view: UserView,
private val userRepository: UserRepository
) : UserPresenter {
override fun loadUser(userId: Int) {
try {
val user = userRepository.getUser(userId)
view.showUser(user)
} catch (e: Exception) {
view.showError("Failed to load user")
}
}
}
最大的改善就是让Presentewr没有Android组件,易于测试。
- MVP明确的将业务逻辑和UI逻辑分开
- Pressenter不依赖于Android组件,使得业务逻辑易于测试
- 三方职责更明确
- 但是View和Presenter间还是存在着较紧密的耦合,View的接口变得复杂了,Presenter的实现也会变得复杂
- 手动把数据绑定到视图也是个不优雅的做法
MVVM 谷歌更推荐的
MVVM是最新的架构,涉及到了Android的Livedata。
Livedata是一个可观察的数据持有者,它能感知组件的生命周期,仅更新处于活动的观察者
Model:数据模型与状态,业务逻辑
View:视图,Activity View Fragment,监听ViewModel的变化并更新UI
ViewModel:处理View的逻辑,与Model交互,不需持有View引用
// Model
data class User(val id: Int, val name: String)
interface UserRepository {
fun getUser(userId: Int): User
}
class UserRepositoryImpl : UserRepository {
override fun getUser(userId: Int): User {
return User(userId, "User$userId")
}
}
// ViewModel
class UserViewModel(private val userRepository: UserRepository) : ViewModel() {
private val _user = MutableLiveData<User>()
val user: LiveData<User> get() = _user
private val _error = MutableLiveData<String>()
val error: LiveData<String> get() = _error
fun loadUser(userId: Int) {
try {
val user = userRepository.getUser(userId)
_user.value = user
} catch (e: Exception) {
_error.value = "Failed to load user"
}
}
}
// View
class UserActivity : AppCompatActivity() {
private lateinit var viewModel: UserViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_user)
val userRepository = UserRepositoryImpl()
viewModel = ViewModelProvider(this, UserViewModelFactory(userRepository))
.get(UserViewModel::class.java)
viewModel.user.observe(this, Observer { user ->
findViewById<TextView>(R.id.userName).text = user.name
})
viewModel.error.observe(this, Observer { message ->
Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
})
viewModel.loadUser(1)
}
}
我个人来看,MVVM用着十分舒服,但是现代的标准衡量,MVP和MVVM都在被广泛使用。