MVC(Model-View-Controller)——一种架构模式,将应用程序分为三个组件:Model负责数据和业务逻辑,View负责用户界面,Controller负责处理输入以及协调Model和View。在iOS中,MVC通过UIViewController实现,在Android中——通过Activity和Fragment实现。MVC仍然是MVVM、MVP和Clean Architecture所基于的基本模式。更多信息——请参阅MVC in Cocoa Core。
要点
MVC(Model-View-Controller)——一种由Trygve Reenskaug于1979年为Smalltalk-80语言提出的架构模式。该模式将应用程序分为三层:Model包含数据和业务逻辑,View负责显示,Controller处理用户输入并更新Model和View。职责分离允许独立更改每一层——例如,将View从UIKit替换为SwiftUI,而无需更改Model中的业务逻辑。
组件的交互在MVC中遵循一个循环:用户与View交互→Controller接收事件→Controller更新Model→Model通知Controller更改→Controller更新View。在经典实现中,Model使用观察者模式:当数据发生变化时,Model发送通知,Controller订阅并更新View。在Apple的实现中,Key-Value Observing(KVO)或NotificationCenter扮演这一角色。
| 组件 | 职责 | iOS中的示例 | Android中的示例 |
|---|---|---|---|
| Model | 数据、业务逻辑、网络 | Struct User、CoreData | Data class、Repository |
| View | UI显示 | Storyboard、XIB、UIView | XML布局、Jetpack Compose |
| Controller | 输入处理、协调 | UIViewController | Activity、Fragment |
MVC在现代移动开发中比10年前使用得更少,但仍然是必须理解的内容。Apple推荐在UIKit应用程序中为简单屏幕使用MVC。Google不推荐在Android中使用纯粹的MVC——官方文档建议使用MVVM与Jetpack。然而,了解MVC对于处理遗留项目和理解架构模式的演变是必要的。
Apple MVC——嵌入在UIKit中的模式的自定义实现。UIViewController扮演Controller的角色:管理屏幕的生命周期(viewDidLoad、viewWillAppear、viewDidDisappear),处理触摸和用户操作,通过IBOutlets更新View。View在Interface Builder(storyboard或XIB)中以编程方式创建。Model——任何数据对象:网络服务、CoreData堆栈、Swift结构体。
final class UserViewController: UIViewController {
// View(通过storyboard outlet)
@IBOutlet private var nameLabel: UILabel!
@IBOutlet private var emailLabel: UILabel!
// Model
private let userService = UserService()
override func viewDidLoad() {
super.viewDidLoad()
loadUser()
}
private func loadUser() {
userService.fetchUser { [weak self] user in
// Controller更新View
self?.nameLabel.text = user.name
self?.emailLabel.text = user.email
}
}
}
Apple MVC的问题——View和Controller紧密耦合。UIViewController同时管理View和逻辑。Storyboard以XML格式存储View,但控制器通过IBOutlets直接引用UI元素。这违反了单一职责原则:控制器负责生命周期、委托、数据源、target-action和动画。结果是,iOS应用的标准屏幕在控制器中包含200–500行代码。
ViewController的生命周期——Apple提供了6个生命周期方法:loadView(手动创建View)、viewDidLoad(View加载到内存后)、viewWillAppear(出现在屏幕之前)、viewDidAppear(动画之后)、viewWillDisappear(离开屏幕之前)、viewDidDisappear(离开之后)。每个方法——在MVC中放置逻辑的地方。将这些方法用于业务逻辑会加速控制器的膨胀。
Android MVC——Activity和Fragment扮演Controller的角色,XML布局文件——View,任何带有数据的POJO类——Model。Activity管理屏幕的生命周期:onCreate、onStart、onResume、onPause、onStop、onDestroy。Fragment——Activity内的子屏幕,具有自己的生命周期。View(XML)与Controller分离,通过setContentView或LayoutInflater加载。Model——仓库、数据库、网络调用。
class UserActivity : AppCompatActivity() {
// View通过XML布局
private lateinit var binding: ActivityUserBinding
// Model
private val userRepository = UserRepository()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityUserBinding.inflate(layoutInflater)
setContentView(binding.root)
loadUser()
}
private fun loadUser() {
userRepository.getUser { user ->
runOnUiThread {
binding.nameText.text = user.name
binding.emailText.text = user.email
}
}
}
}
Android ViewBinding和DataBinding——减少Controller和View耦合的现代工具。ViewBinding生成一个类,直接从XML引用View,消除了findViewById。DataBinding增加了通过@{user.name}在XML标记中将数据与UI绑定的能力。DataBinding——迈向MVVM的一步,因为它允许在没有Activity代码的情况下将数据从Model传输到View。Google推荐所有新项目使用DataBinding。
Android的生命周期比iOS更复杂:Activity可能在屏幕旋转、内存不足或配置更改时被销毁和重新创建。在纯粹的MVC中,控制器(Activity)包含在销毁时会丢失的逻辑。这需要通过onSaveInstanceState或Jetpack中的ViewModel保存状态,这超出了纯粹MVC的框架,使架构更接近MVVM。
Massive View Controller——描述MVC在移动开发中的主要问题的术语。iOS和Android中的控制器承担了太多职责:输入处理、数据验证、网络交互、导航、缓存、动画、生命周期管理。结果,控制器膨胀到500–2000行代码,变得难以阅读、测试和维护。
Massive View Controller的原因——UIKit和Android Framework的架构鼓励将逻辑放在控制器中。网络调用、JSON处理、导航——所有这些自然地在Activity或UIViewController中编写,因为它们可以访问生命周期和UI。开发人员必须有意识地将逻辑提取到单独的类(Service、Manager、Interactor)中,这需要纪律和对架构原则的理解。
| MVC问题 | 描述 | 解决方案 |
|---|---|---|
| 强耦合 | Controller知道View和Model | MVVM——ViewModel不知道View |
| 测试困难 | Controller依赖UIKit/Android | 将逻辑提取到服务中 |
| 生命周期 | 状态在旋转时丢失 | Jetpack/SwiftUI中的ViewModel |
| 缺乏导航 | Controller管理转场 | Coordinator模式、Router |
MVC的测试——Model通过单元测试进行隔离测试。由于依赖UIKit/UIFoundation,Controller难以测试。XCTest不允许在没有视图窗口的情况下创建UIViewController。对于Android,ActivityTestRule和Robolectric部分解决了问题,但测试速度较慢。View通常不通过单元测试进行测试——UI使用截图测试和UI测试(XCUITest、Espresso)。
何时使用MVC是合理的——包含一两个元素的简单屏幕(登录屏幕、个人资料、设置)。用于验证假设的原型和MVP——MVC无需额外层即可更快编写。代码库小到10–15个屏幕的项目。在复杂项目中,MVC会导致技术债务的积累,并需要每6–12个月进行重构。
MVC vs MVVM——主要区别:在MVVM中,控制器被ViewModel取代,ViewModel没有对View的引用。数据通过Observable(SwiftUI)、LiveData/StateFlow(Android)或Combine/RxSwift传递。ViewModel在没有UI依赖的情况下通过单元测试进行测试。Apple自2019年起推荐MVVM与SwiftUI,Google推荐MVVM与LiveData/Flow作为Android的官方架构。MVVM需要更多代码进行绑定,但显著提高了可测试性。
MVC vs MVP——在MVP(Model-View-Presenter)中,Presenter是一个可测试的层,通过接口接收View。与MVC不同,MVC中Controller直接通过UIKit管理View,而Presenter不依赖于框架——通过ViewInterface抽象工作。MVP在Jetpack出现之前在Android开发中很流行,并用于旧项目。Presenter比Activity存活更久,并在屏幕旋转时保持状态。
MVC vs Clean Architecture——Clean Architecture添加了Use Cases(Interactors)、Entities、Gateways和Repository层。MVC保留在展示层,但业务逻辑被移动到领域层,使用Use Cases。Clean Architecture从根本上解决了Massive View Controller问题——Controller只包含Use Cases调用和View更新。缺点——类和文件数量显著增加,这对于50+个屏幕的项目是合理的。
// iOS中的MVC:Controller包含一切
class OrderViewController: UIViewController {
func placeOrder() {
// 验证 + 网络 + UI更新
guard Validation.isValid(total) else { return }
NetworkService.shared.submit(order) { [weak self] result in
self?.handleResult(result)
}
}
}
// MVVM:逻辑在ViewModel中
class OrderViewModel: ObservableObject {
@Published var state: OrderState = .idle
func placeOrder() { /* 业务逻辑 */ }
}
架构的选择取决于团队规模、项目和所需的可测试性。对于1–2名开发人员的团队和最多20个屏幕的项目,MVVM是合适的。对于5名以上开发人员的大型团队和50个屏幕以上的项目——具有模块化结构的Clean Architecture。MVC仍然具有现实意义用于理解架构的演变、支持遗留项目以及在没有复杂业务逻辑的UIKit中构建简单屏幕。
常见问题
主要问题是Massive View Controller。在iOS中,UIViewController控制器负责一切:输入处理、更新View、网络工作、导航和生命周期。在Android中,Activity/Fragment执行类似的功能。结果,控制器膨胀到数千行代码,变得难以测试和维护,违反了单一职责原则。
在MVC中,控制器直接更新View和处理用户输入。在MVVM中,控制器的角色由ViewModel承担,ViewModel没有对View的引用——数据通过绑定机制传递。MVVM更容易测试,因为ViewModel不依赖于UIKit或Android Framework。Apple推荐MVVM与SwiftUI,Google推荐MVVM与Jetpack Compose。
是的,MVC仍然是简单屏幕和原型的工作模式。Apple推荐对具有简单屏幕的UIKit应用程序使用MVC。对于具有多个屏幕、网络请求和缓存的复杂项目,最好选择MVVM、VIPER或Clean Architecture。建议初学者在学习更复杂的模式之前先掌握MVC。
Model可以隔离测试——它们是普通的数据对象和业务逻辑。由于依赖UIKit或Android Framework,Controller难以测试。建议将业务逻辑从控制器提取到单独的服务或交互器中,这些服务或交互器通过单元测试进行测试。View通常不通过单元测试进行测试——使用UI测试和截图测试。
在iOS中——MVVM与SwiftUI和Combine,自2019年以来的Apple标准。在Android中——MVVM与LiveData或StateFlow,Google官方推荐。对于拥有5名以上开发人员团队的大型项目——iOS上使用VIPER的Clean Architecture或Android上按功能划分模块的Clean Architecture。对于使用MVC的遗留项目——逐步重构,将逻辑提取到单独的服务中。
总结
我们将开发一款交钥匙移动应用程序
IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。