zoukankan      html  css  js  c++  java
  • 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()
        }
    }

     

     

     

  • 相关阅读:
    IDEA 'Error:java: 无效的源发行版: 12' 解决方案
    E Golang语言之网络编程
    E 04 Golang语言之运算符
    ie表单提交提示下载文件
    日期初始化兼容
    IE8兼容问题总结---trim()方法
    es6变量声明和解构赋值
    js的call和apply拾遗
    prop&attr区别和用法,以多选框为例
    es6的箭头函数
  • 原文地址:https://www.cnblogs.com/StevenHuSir/p/AppArchitecture.html
Copyright © 2011-2022 走看看