生也有涯,知也无涯。这篇文章主要讲述AppArchitecture-8种相关的知识,希望能为你提供帮助。
{ margin: 0;
font: 14px "Yuanti SC" }
p.p2 { margin: 0;
font: 18px "Yuanti SC" }
p.p3 { margin: 0;
font: 14px "Yuanti SC";
color: rgba(0, 0, 0, 1) }
span.s1 { color: rgba(251, 2, 7, 1) }
span.s2 { font: 14px "Lucida Grande" }
span.s3 { color: rgba(0, 0, 255, 1) }
span.s4 { background-color: rgba(255, 255, 255, 1) }
span.s5 { color: rgba(0, 0, 0, 1) }
span.s6 { color: rgba(153, 102, 51, 1) }App架构:(Swift)
App设计模式:
Coordinator[k??\'?:d?ne?t?] 协调者
?Model-View-Controller(MVC)
?Model-View-ViewModel+Coordinator(MVVM-C)
?Model-View-Controller+ViewState(MVC+VC)
?ModelAdapter-ViewBinder(MAVB)模型适配器+视图绑定
?Elm架构(The ElmArchitecture, TEA)
Github地址:https://github.com/objcio/app-architecture
UISplitViewController类时一个容器视图控制器,它显示一个master-detail界面(主界面、详情界面)。
应用架构:
App架构是软件设计的一个分支,它关心如何设计一个App的结构。
其实就是一套分类,app中不同的部件会被归纳到某个类型中去。层次:遵循一些基本规则并负责特定功能的接口和其他代码的集合。
简单设计一个功能,向文本框中输入文本,点击Commit提交,使输入文本显示在textField上,动态获取它的值
文章图片
import Foundation class Model {static let textDidChange = Notification.Name("textDidChange") static let textKey = "text"var value: String { didSet { NotificationCenter.default.post(name: Model.textDidChange, object: self, userInfo: [Model.textKey: value]) } }init(value: String) { self.value = https://www.songbingjia.com/android/value } }
p.p1 { margin: 0; font: 14px "Yuanti SC"; color: rgba(0, 0, 0, 1) }①MVC架构
必须设置初始值,然后观察他,当输入文本变化时,Model发送通知,Controller接收通知,修改View的状态数据
class MultiViewController: UIViewController, UITextFieldDelegate { let model = Model(value: "initial value") var mvcTextField: UITextField! var mvcButton: UIButton! // Strong references var mvcObserver: NSObjectProtocol? override func viewDidLoad() { super.viewDidLoad() mvcTextField.text = model.value mvcObserver = NotificationCenter.default.addObserver(forName: Model.textDidChange, object: nil, queue: nil) { [mvcTextField] (note) in mvcTextField?.text = note.userInfo?[Model.textKey] as? String } //First,set the initial value(必须设置初始值) //Then,observe it(然后观察他) //And you also have to combine with view state(并且从view到model的路径上你还必须结合view的状态数据) } func mvcButtonPressed() { model.value = https://www.songbingjia.com/android/mvcTextField?.text ??"" } }
p.p1 { margin: 0; font: 14px "Yuanti SC"; color: rgba(0, 0, 0, 1) }②MVP架构
避免将controller的逻辑与view的实现偶合在一起,所以你把这部分逻辑提取出来,放到另一个对象中,这个对象只通过协议和view进行通信。(跟MVC类似,只是多了一个协议作为边界)
class MultiViewController: UIViewController, UITextFieldDelegate { let model = Model(value: "initial value") mvpTextField: UITextField! var mvpButton: UIButton! var presenter: ViewPresenter? override func viewDidLoad() { super.viewDidLoad() mvpDidLoad() } } //①避免将 controller的逻辑与view的实现耦合在一起,所以你把这部分逻辑提取出来,放到另外一个对象中,这个对象只通过协议和View进行通信 //②这样如果你需要的话,可以用替换协议层来做测试 //③跟MVC类似,只是多了一个协议作为边界protocol ViewProtocol: class { var textFieldValue: String { get set } }class ViewPresenter { let model: Model weak var view: ViewProtocol? let observer: NSObjectProtocol //观察者//初始化方法设置model和view init(model: Model, view: ViewProtocol) { self.model = model self.view = viewview.textFieldValue = https://www.songbingjia.com/android/model.value observer = NotificationCenter.default.addObserver(forName: Model.textDidChange, object: nil, queue: nil) { [view] (note) in view.textFieldValue = note.userInfo?[Model.textKey] as? String ??"" } }func commit() {//提交 model.value = https://www.songbingjia.com/android/view?.textFieldValue ??"" } }extension MultiViewController: ViewProtocol { func mvpDidLoad() { presenter = ViewPresenter(model: model, view: self) //简化ViewController代码 } //协议 var textFieldValue: String { get { return mvpTextField.text ?? "" } set { mvpTextField.text = newValue } }func mvpButtonPressed() { presenter?.commit() } }
p.p1 { margin: 0; font: 14px "Yuanti SC"; color: rgba(0, 0, 0, 1) }③极简MVVM+不加RAC
class MultiViewController: UIViewController, UITextFieldDelegate { let model = Model(value: "initial value") var mvvmmTextField: UITextField! var mvvmmButton: UIButton! // Strong references var minimalViewModel: MinimalViewModel? var minimalObserver: NSObjectProtocol? override func viewDidLoad() { super.viewDidLoad() mvvmMinimalDidLoad() } } // Minimal MVVM class MinimalViewModel: NSObject { let model: Model var observer: NSObjectProtocol? @objc dynamic var textFieldValue: String //@objc 可以被objc观察到 (KVO动态绑定)init(model: Model) { self.model = model textFieldValue = https://www.songbingjia.com/android/model.value //初始阿化值 super.init() observer = NotificationCenter.default.addObserver(forName: Model.textDidChange, object: nil, queue: nil) { [weak self] (note) in self?.textFieldValue = note.userInfo?[Model.textKey] as? String ??"" }} //我们不能从View中获取它,我们需要的时候,我们需要view传给我们 func commit(value: String) { model.value = https://www.songbingjia.com/android/value } }extension MultiViewController { func mvvmMinimalDidLoad() { minimalViewModel = MinimalViewModel(model: model) minimalObserver = minimalViewModel?.observe(\\.textFieldValue, options: [.initial, .new], changeHandler: { [weak self] (_, change) in self?.mvvmmTextField.text = change.newValue }) }func mvvmmButtonPressed() { minimalViewModel?.commit(value: mvvmmTextField.text ??"") } }
p.p1 { margin: 0; font: 14px "Yuanti SC"; color: rgba(0, 0, 0, 1) }④MVVM
class MultiViewController: UIViewController, UITextFieldDelegate { let model = Model(value: "initial value") var mvvmTextField: UITextField! var mvvmButton: UIButton! // Strong references var viewModel: ViewModel? var mvvmObserver: Cancellable? override func viewDidLoad() { super.viewDidLoad() mvvmDidLoad() } } // MVVM -加RAC(响应式编程)CwlSignal class ViewModel { let model: Modelvar textFieldValue: Signal< String> { return Signal .notifications(name: Model.textDidChange) //compactMap 是map操作,又会过滤nil值并解包可选值, 因为我们将必须做一串optional chaining才能获取到值 .compactMap { note in note.userInfo?[Model.textKey] as? String } //因为我们想要在信号来之前也能接收最新的值,并且我们还想要设置初始化值,所以我们要把信号的输出变成continuous的 .continuous(initialValue: model.value) //我们现在可以从model经过compactMap到continuous来遍历数据流,最后观察出这个输出; //所以数据流将持续传递给我们 }init(model: Model) { self.model = model }func commit(value: String) { model.value = https://www.songbingjia.com/android/value } }extension MultiViewController { func mvvmDidLoad() { viewModel = ViewModel(model: model) //数据绑定 //添加观察 subscribeValues 订阅值 mvvmObserver = viewModel!.textFieldValue .subscribeValues { [unowned self] (str) in self.mvvmTextField.text = str } } //它会追踪从model到每个暴露出来的可观察变量的路径 @IBAction func mvvmButtonPressed() { viewModel?.commit(value: self.mvvmTextField.text ??"") } }
p.p1 { margin: 0; font: 14px "Yuanti SC"; color: rgba(0, 0, 0, 1) }参考:https://www.jianshu.com/p/545f2b94ee3d
⑤MVC+VC
class MultiViewController: UIViewController, UITextFieldDelegate { let model = Model(value: "initial value") var mvcvsTextField: UITextField! var mvcvsButton: UIButton! // Strong references var viewState: ViewState? var viewStateModelObserver: NSObjectProtocol? var viewStateObserver: NSObjectProtocol? override func viewDidLoad() { super.viewDidLoad() mvcvsDidLoad() } } // MVC+VS --------------------------------------------------------- // MVC + ViewState(全局) //确保任何action所需要的状态已经从view中剥离出来,所以任何时候,我们都不依靠view来存储数据。 //所以当一个事件发生时,view回去改变viewS state 例如:commit操作,我们不依赖于view的数据, 我们不从view获取数据,而是从view state中获取它、 //对那些对于我们来说很重要的状态,我们总是能立即在代码里将它表示出来。 class ViewState {//全局共享 var textFieldValue: String = "" //观察textField的值,当它发生变化时,我们将需要把内容更新到刚才创建的view state中去 init(textFieldValue: String) { self.textFieldValue = https://www.songbingjia.com/android/textFieldValue } }extension MultiViewController { func mvcvsDidLoad() { viewState = ViewState(textFieldValue: model.value) mvcvsTextField.text = model.value//设置初始化值 viewStateObserver = NotificationCenter.default.addObserver(forName: .UITextFieldTextDidChange, object: mvcvsTextField, queue: nil, using: { [viewState] n in viewState?.textFieldValue = (n.object as! UITextField).text ??"" }) viewStateModelObserver = NotificationCenter.default.addObserver(forName: Model.textDidChange, object: nil, queue: nil, using: { [mvcvsTextField] n in mvcvsTextField?.text = n.userInfo?[Model.textKey] as? String }) //更加精确地观察view,当它发生改变时,捕获这个状态 }@IBAction func mvcvsButtonPressed() { model.value = https://www.songbingjia.com/android/viewState?.textFieldValue ??"" } }
p.p1 { margin: 0; font: 14px "Yuanti SC"; color: rgba(0, 0, 0, 1) }⑥Elm架构
class MultiViewController: UIViewController, UITextFieldDelegate { let model = Model(value: "initial value") var driver: Driver< ElmState, ElmState.Action> ? override func viewDidLoad() { super.viewDidLoad() elmViewDidLoad() } } //The Elm Architecture 不依靠storyboard来创建按钮和文本框 //如果你想和任意的view或者state打交道,你要明确创建它(需要构建其他的View,其他架构则不需要) 便于测试 //对于View,你能构建一个State然后调用来生成View 测试你是否拥有正确的View层次结构 //Elm ------------------------------------------------- struct ElmState { var text: String //虚拟构建view //模拟器中缺失的这些View,是水平方向的stackView中的 enum Action { case commit case setText(String) case modelNotification(Notification) }//给定一个action,对状态进行更新的方法 mutating func update(_ action: Action) -> Command< Action> ? { switch action { case .commit: return .changeModelText(text) case .setText(let text): self.text = text return nil case .modelNotification(let note): text = note.userInfo?[Model.textKey]as? String ?? "" return nil } }var view: [ElmView< Action> ] { return [ ElmView.textField(text, onChange: Action.setText),//setText将实时获得文本框的改变 ElmView.button(title: "Commit", onTap: Action.commit)//单独的按钮操作 ] }//订阅获取值 var subscriptions: [Subscription< Action> ] { return [ .notification(name: Model.textDidChange, Action.modelNotification) ] } }extension MultiViewController { func elmViewDidLoad() { //驱动stackView driver = Driver.init(ElmState(text: model.value), update: { state, action in state.update(action) }, view: { $0.view }, subscriptions: { $0.subscriptions }, rootView: stackView, model: model) } }
p.p1 { margin: 0; font: 14px "Yuanti SC"; color: rgba(0, 0, 0, 1) }⑦MAVB
class MultiViewController: UIViewController, UITextFieldDelegate { let model = Model(value: "initial value") var viewStateAdapter: Var< String> ! override func viewDidLoad() { super.viewDidLoad() mavbDidLoad() } } // MAVB --------------------------------------------------------- extension MultiViewController { func mavbDidLoad() { viewStateAdapter = Var(model.value)TextField( .text < -- Signal .notifications(name: Model.textDidChange) .compactMap { note in note.userInfo?[Model.textKey] as? String } .startWith(model.value), .didChange --> Input().map { $0.text }.bind(to: viewStateAdapter) ).applyBindings(to: mavbTextField)//将文本描述绑定到textField上Button( .action(.primaryActionTriggered) --> Input() //primaryActionTriggered 按钮出发一次,就会获取一次值(view state) , 更新model .trigger(viewStateAdapter) .subscribeValuesUntilEnd { [model] value in model.value = https://www.songbingjia.com/android/value } ).applyBindings(to: mavbButton) } }
p.p1 { margin: 0; font: 14px "Yuanti SC"; color: rgba(0, 0, 0, 1) }⑧Clean Architecture
class MultiViewController: UIViewController, UITextFieldDelegate { let model = Model(value: "initial value") var cleanPresenter: CleanPresenter! override func viewDidLoad() { super.viewDidLoad() cleanDidLoad() } } // "Clean" --------------------------------------------------------- protocol CleanPresenterProtocol: class { var textFieldValue: String { get set } }class CleanUseCase { let model: Model var modelValue: String { get { return model.value } set { model.value = https://www.songbingjia.com/android/newValue } } weak var presenter: CleanPresenterProtocol? var observer: NSObjectProtocol? init(model: Model) { self.model = model observer = NotificationCenter.default.addObserver(forName: Model.textDidChange, object: nil, queue: nil, using: { [weak self] n in self?.presenter?.textFieldValue = n.userInfo?[Model.textKey] as? String ??"" }) } }protocol CleanViewProtocol: class { var cleanTextFieldValue: String { get set } }class CleanPresenter: CleanPresenterProtocol { let useCase: CleanUseCase weak var view: CleanViewProtocol? { didSet { if let v = view { v.cleanTextFieldValue = https://www.songbingjia.com/android/textFieldValue } } } init(useCase: CleanUseCase) { self.useCase = useCase self.textFieldValue = useCase.modelValue useCase.presenter = self}var textFieldValue: String { didSet { view?.cleanTextFieldValue = textFieldValue } }func commit() { useCase.modelValue = view?.cleanTextFieldValue ??"" } }extension MultiViewController: CleanViewProtocol { var cleanTextFieldValue: String { get { return cleanTextField.text ?? "" } set { cleanTextField.text = newValue } }func cleanDidLoad() { let useCase = CleanUseCase(model: model) cleanPresenter = CleanPresenter(useCase: useCase) cleanPresenter.view = self } @IBAction func cleanButtonPressed() { cleanPresenter.commit() } }
【AppArchitecture-8种】
推荐阅读
- "小说阅读神器"App-隐私政策
- Swift可选链介绍和用法示例
- SwiftyJSON介绍和开发示例详解
- Swift中的计时器解释和用法示例
- Swift 4类型转换介绍和用法示例
- Swift 4初始化介绍和用法示例
- Swift反初始化介绍和用法
- Swift 4下标用法示例
- Swift中的继承介绍和使用示例