AppArchitecture-8种

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 = value
    }
}

①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 = mvcTextField?.text ?? ""
    }
}

②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 = view
        
        view.textFieldValue = 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 = 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()
    }
}

③极简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 = 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 = 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 ?? "")
    }
}

④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: Model
    
    var 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 = 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 ?? "")
    }
}

参考: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 = 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 = viewState?.textFieldValue ?? ""
    }
}

⑥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)
    }
}

⑦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 = value }
        ).applyBindings(to: mavbButton)
    }
}

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 = 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 = 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()
    }
}

 

 

 

 

posted @ 2018-10-16 10:29  淡然微笑_Steven  阅读(344)  评论(0编辑  收藏  举报