MVC
MVC 是一种将应用程序分为三部分的架构模式:Model(模型)、View(视图)、Controller(控制器)。每部分负责不同的职责,使代码更加模块化、易于维护。
• Model:负责业务数据的管理,包含业务数据的结构和逻辑。以及数据存储。
• View:负责界面的展示和用户交互。
• Controller:负责连接 Model 和 View,处理用户输入并更新 Model 和 View。
其中Controller是Model和View的桥梁,见下图:
整个框架的设计最优结构是View层不依赖Controller层而独立存在,Model层不依赖Controller层和View层独立存在,Controller层负责关联二者,View层只负责展示,Model层持有数据和业务的具体实现,而Controller层则处理事件响应以及业务的调用以及通知界面更新。三者之间一定要明确的定义为单向依赖,而不应该出现双向依赖
Swift demo
import UIKit
// Model: 数据结构
struct User {
let name: String
let age: Int
func isAdult() -> Bool {
return age >= 18
}
}
// View: 展示用户信息
class UserView: UIView {
private let nameLabel = UILabel()
private let ageLabel = UILabel()
override init(frame: CGRect) {
super.init(frame: frame)
setupView()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
setupView()
}
private func setupView() {
addSubview(nameLabel)
addSubview(ageLabel)
// ...布局代码省略
}
func updateView(name: String, age: Int) {
nameLabel.text = "Name: \(name)"
ageLabel.text = "Age: \(age)"
}
}
// Controller: 连接 Model 和 View
class UserController: UIViewController {
private let userView = UserView()
private var user: User?
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(userView)
// ...布局代码省略
// 设置 Model 并更新 View
userView.updateView(name: "Alice", age: 25)
}
}
好处:提高开发效率,简化操作
坏处:
• UIViewController 变得过于臃肿,因为它承担了太多职责。
• View 和 Controller 间的耦合度较高,难以测试。
MVP
MVP 是一种改进的 MVC 设计模式,它通过 Presenter 更清晰地管理 View 和 Model 之间的交互。MVP 中 View 和 Model 的依赖关系较少,逻辑处理更加集中在 Presenter 中。MVP 模式通过引入 Presenter 进一步解耦,将业务逻辑与视图更新独立出来。具体来说:
• Model:负责数据处理和业务逻辑,与 MVC 中的 Model 类似。它处理所有与数据相关的操作,比如获取、保存和更新数据。
• View:只负责显示 UI 和用户的输入,尽可能简单。它不直接与 Model 交互,而是通过 Presenter 进行通信。View 不包含任何业务逻辑。
• Presenter:是 View 和 Model 的中间层,负责从 Model 获取数据并传递给 View,同时处理用户输入和更新 Model。Presenter 不依赖于具体的 UI 框架,因此更易于测试。
结构如下:
在 MVP 中,View 非常轻量,几乎不包含任何业务逻辑。所有与 View 相关的业务逻辑都交给 Presenter 来处理。View 和 Model 之间没有直接交互,所有交互都通过 Presenter。
优点:
• Presenter 不依赖具体的 View,实现了业务逻辑的分离,增强了代码的可测试性。
缺点:
• 由于通过协议来定义 View,会带来额外的代码复杂度。
MVVM
MVVM 是一种更注重数据绑定和解耦的架构模式,分为三部分:Model(模型)、View(视图)、ViewModel(视图模型)。其中视图模型(ViewModel)其实就是MVP模式中的P
• Model:和 MVC 中类似,负责数据管理。
• View:负责界面的展示。
• ViewModel:负责处理 Model 和 View 的逻辑,通常包含数据的转换、绑定等,使 View 更加轻量化。
swift demo
ViewModel & Model
import UIKit
// MARK: - Model
public class Pet {
public enum Rarity {
case common
case uncommon
case rare
case veryRare
}
public let name: String
public let birthday: Date
public let rarity: Rarity
public let image: UIImage
public init(name: String,
birthday: Date,
rarity: Rarity,
image: UIImage) {
self.name = name
self.birthday = birthday
self.rarity = rarity
self.image = image
}
}
// MARK: - ViewModel:把Model的业务逻辑写在ViewModel中,等于是ViewModel在Model的基础上按照业务逻辑再次封装
public class PetViewModel {
// 创建两个属性,并在初始化方法中设值。
private let pet: Pet
private let calendar: Calendar
public init(pet: Pet) {
self.pet = pet
self.calendar = Calendar(identifier: .gregorian)
}
// 声明 name 和 image 为计算属性。
public var name: String {
return pet.name
}
public var image: UIImage {
return pet.image
}
// 计算属性转换后,将可以使用显示。
public var ageText: String {
let today = calendar.startOfDay(for: Date())
let birthday = calendar.startOfDay(for: pet.birthday)
let components = calendar.dateComponents([.year],
from: birthday,
to: today)
let age = components.year!
return "\(age) years old"
}
// 根据 rarity 决定价格。
public var adoptionFeeText: String {
switch pet.rarity {
case .common:
return "$50.00"
case .uncommon:
return "75.00"
case .rare:
return "150.00"
case .veryRare:
return "$500.00"
}
}
}
// MARK: ViewModel :负责处理 Model 和 View 的逻辑,通常包含数据的转换、绑定等,使 View 更加轻量化。
extension PetViewModel{
public func configView(view : PetView){
view.nameLabel.text = self.name
view.imageView.image = self.image
view.ageLabel.text = self.ageText
view.adoptionFeeLabel.text = self.adoptionFeeText
}
}
View (View)
import UIKit
// MARK: - View : View只负责UI
public class PetView: UIView {
public let imageView: UIImageView
public let nameLabel: UILabel
public let ageLabel: UILabel
public let adoptionFeeLabel: UILabel
public override init(frame: CGRect) {
var childFrame = CGRect(x: 0,
y: 16,
width: frame.width,
height: frame.height / 2)
imageView = UIImageView(frame: childFrame)
imageView.contentMode = .scaleAspectFit
childFrame.origin.y += childFrame.height + 16
childFrame.size.height = 30
nameLabel = UILabel(frame: childFrame)
nameLabel.textAlignment = .center
childFrame.origin.y += childFrame.height
ageLabel = UILabel(frame: childFrame)
ageLabel.textAlignment = .center
childFrame.origin.y += childFrame.height
adoptionFeeLabel = UILabel(frame: childFrame)
adoptionFeeLabel.textAlignment = .center
super.init(frame: frame)
backgroundColor = .white
addSubview(imageView)
addSubview(nameLabel)
addSubview(ageLabel)
addSubview(adoptionFeeLabel)
}
@available(*, unavailable)
public required init?(coder aDecoder: NSCoder) {
fatalError("init?(coder:) is not supported")
}
}
Controller
import UIKit
class MVVMController: UIViewController {
override func viewDidLoad(){
super.viewDidLoad()
self.view.backgroundColor = .white
// MARK: - Example
let birthday = Date(timeIntervalSinceNow: (-3 * 86400 * 366))
let image = UIImage(named: "linedog")!
let Linedog = Pet(name: "Linedog",
birthday: birthday,
rarity: .veryRare,
image: image)
// 使用 linedog 创建 viewModel
let viewModel = PetViewModel(pet: Linedog)
let frame = CGRect(x: 0,
y: 0,
width: 300,
height: 420)
let petView = PetView(frame: frame)
// 使用 viewModel 直接显示
viewModel.configView(view: petView)
self.view.addSubview(petView)
}
}
优点:
• View 和业务逻辑的完全分离,增强了代码的可测试性。
• 数据绑定使得 UI 更新非常方便。
缺点:
• 初期代码复杂度较高,尤其是在没有使用响应式编程的情况下。
MVC和MVVM中的View最好都是只负责UI展示,让MVC的Model和MVVM的ViewModel来处理 Model 和 View 的逻辑 。
如果有多个View在使用此 ViewModel,把所有显示逻辑放到 ViewModel 会让 ViewModel 混乱。在这种情况下,为每个View单独配置显示代码(Model 和 View 的逻辑 )可能更为简洁。
mvc的Controller和mvp的Presenter和mvvm的viewmodel的主要区别:
主要是view的交互、与model的交互区别。
总结:
参考:
https://medium.com/@wanghaonanlpc/mvc-vs-mvp-vs-mvvm-bfcf7568aac0