安卓架构 -- 有关MVC、MVP、MVVM 或许我并不是那么懂它们

前言-- 应用架构

初次接触编程时相信就算是最天才的程序员也是把所有代码(甚至是几千行)写在一个文件里,但是慢慢的我们会发现其实这样是很不好的,一个月后或许就连自己看这些代码都会发现,卧槽这是写的啥。

只要是公司级别的项目,代码开发都是多人合作,虽然特定的模块常有特定的团队维护,但是不符合标准的代码也是不利于传承的,团队的人员会变迁,团队也会变迁。

应用架构是一个很重要的指标,良好的应用架构=更快定位错误代码 + 新人更好快速上手 + 更好的应用


在Android,被人们说烂了的架构叫MVC,MVP,MVVC,但是我写下这篇文章的原因,是因为我发现我并不是完全、真正懂这三个架构。

正文-- 你真的懂它们吗

SRP原则-- 单一责任原则

MVC 梦开始的地方

最古老的架构就是MVC,Android系统从宏观来看也是MVC
Model: 数据模型与状态,包含应用的业务逻辑
View: 视图,把数据表现出来,即布局
Controller: 模型和视图之间的通道,负责反映用户的交互
MVC

  1. 用户通过 View进行交互。
  2. View 将用户的交互信息传递给 Controller。
  3. Controller 接收到信息后,调用 Model 更新数据或执行业务逻辑。
  4. Model 更新数据后,通知 Controller 数据已经改变。
  5. 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进行显示
MVP

// 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引用
MVVM

// 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都在被广泛使用。

  • 14
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

夏目艾拉

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值