为什么要纠结选择什么架构?
假如有一天, 你在调试一个实现了几十种功能的庞大的类时, 你会发现自己很难找到并修复你的类中的任何错误. 并且, 很难把这个类作为一个整体来考虑, 因此, 你总会忽略一些重要的细节. 如果你的应用程序中已经出现了这种情况, 那么很有可能:
1. 这类是控制器类
2. 控制器直接存储和处理你的数据
3. 你的 Views 几乎没有做任何事情
4.Model 仅仅是一个数据结构
5. 单元测试覆盖不了任何内容
说到设计模式, 我们先来看看 App 架构的设计原则
单一职责原则永远不应该有多于一个原因来改变某个类. 对于一个类而言, 应该仅有一个引起它变化的原因. 说白了就是, 不同的类具备不同的职责, 各施其责. 这就好比一个团队, 大家分工协作, 互不影响, 各做各的事情.
开放封闭原则
软件实体, 如: 类, 模块与函数, 对于扩展应该是开放的, 但对于修改应该是封闭的. 接口只增不轻易删改. 当需求有改动, 要修改代码了, 此时您要做的是, 尽量用继承或组合的方式来扩展类的功能, 而不是直接修改类的代码. 当然, 如果能够确保对整体架构不会产生任何影响, 那么也没必要搞得那么复杂了, 直接改这个类吧.
里氏替换原则
使用基类的指针或引用的函数, 必须是在不知情的情况下, 能够使用派生类的对象. 父类能够替换子类, 但子类不一定能替换父类. 也就是说, 在代码中可以将父类全部替换为子类, 程序不会报错, 也不会在运行时出现任何异常, 但反过来却不一定成立. 在继承类时, 务必重写 (Override) 父类中所有的方法, 尤其需要注意父类的 protected 方法(它们往往是让您重写的), 子类尽量不要暴露自己的 public 方法供外界调用.
最少知识原则
尽量减少对象之间的交互, 从而减小类之间的耦合. 简言之, 一定要做到: 低耦合, 高内聚. 在做系统设计时, 不要让一个类依赖于太多的其他类, 需尽量减小依赖关系.
依赖倒置原则
高层模块不应该依赖于低层模块, 它们应该依赖于抽象. 抽象不应该依赖于细节, 细节应该依赖于抽象. 应该面向接口编程, 不应该面向实现类编程. 面向实现类编程, 相当于就是论事, 那是正向依赖(正常人思维); 面向接口编程, 相当于通过事物表象来看本质, 那是反向依赖, 即依赖倒置(程序员思维)
(其他设计原则)
不要重复你自己
不要让重复的代码到处都是, 要让它们足够的重用, 所以要尽可能地封装.
保持它简单与傻瓜
不要让重复的代码到处都是, 要让它们足够的重用, 所以要尽可能地封装.
高内聚与低耦合
模块内部需要做到内聚度高, 模块之间需要做到耦合度低.
惯例优于配置
尽量让惯例来减少配置, 这样才能提高开发效率, 尽量做到 "零配置". 很多开发框架都是这样做的.
命令查询分离
在定义接口时, 要做到哪些是命令, 哪些是查询, 要将它们分离, 而不要揉到一起.
关注点分离
将一个复杂的问题分离为多个简单的问题, 然后逐个解决这些简单的问题, 那么这个复杂的问题就解决了. 难就难在如何进行分离.
契约式设计
模块或系统之间的交互, 都是基于契约 (接口或抽象) 的, 而不要依赖于具体实现. 该原则建议我们要面向契约编程.
常用设计模式
- MVC Model - View - Controller
- MVVM Model-View-ViewModel
- MVP Model - View - Presenter
引用的网络图片
1)Model 和 View 永远不能相互通信, 只能通过 Controller 传递.
2)Controller 可以直接与 Model 对话(读写调用 Model),Model 通过 Notification 和 KVO 机制与 Controller 间接通信.
3)Controller 可以直接与 View 对话, 通过 outlet, 直接操作 View,outlet 直接对应到 View 中的控件, View 通过 action 向 Controller 报告事件的发生(如用户 Touch 我了).Controller 是 View 的直接数据源(数据很可能是 Controller 从 Model 中取得并经过加工了).Controller 是 View 的代理(delegate), 以同步 View 与 Controller.
MVC 自身不足
MVC 在现实应用中的不足在 MVC 模式中 view 将用户交互通知给控制器. view 的控制器通过更新 Model 来反应状态的改变. Model(通常使用 Key-Value-Observation)通知控制器来更新他们负责的 view.
愈发笨重的 Controller 在传统的 App 中模型数据一般都很简单, 不涉及到复杂的业务数据逻辑处理, 客户端开发受限于它自身运行的的平台终端, 这一点注定使移动端不像 PC 前端那样能够处理大量的复杂的业务场景. 然而随着移动平台的各种深入, 我们不得不考虑这个问题. 传统的 Model 数据大多来源于网络数据, 拿到网络数据后客户端要做的事情就是将数据直接按照顺序画在界面上. 随着业务的越来越来的深入, 我们依赖的 service 服务可能在大多时间无法第一时间满足客户端需要的数据需求, 移动端愈发的要自行处理一部分逻辑计算操作. 这个时间一惯的做法是在控制器中处理, 最终导致了控制器成了垃圾箱, 越来越不可维护.
太过于轻量级的 Model: 早期的 Model 层, 其实就是如果数据有几个属性, 就定义几个属性, ARC 普及以后我们在 Model 层的实现文件中基本上看不到代码(无需再手动管理释放变量, Model 既没有复杂的业务处理, 也没有对象的构造, 基本上 .m 文件中的代码普遍是空的); 同时与控制器的代码越来厚重形成强烈的反差, 这一度让人不禁对现有的开发设计构思有所怀疑
遗失的网络逻辑: 苹果使用的 MVC 的定义是这么说的: 所有的对象都可以被归类为一个 Model, 一个 view, 或是一个控制器. 就这些, 那么把网络代码放哪里? 和一个 API 通信的代码应该放在哪儿? 所以我们的 MVVM 设计模式也就应运而生了.
MVVM 设计模式
引用的网络图片
M: 对应于 MVC 的 M
V: 对应于 MVC 的 V
VM:viewModel, 是把 MVC 里的 controller 的数据的加载, 加工功能分离出来
MVVM 是 web 前端一种非常流行的开发模式, 利用 MVVM 可以使我们的代码更专注于处理业务逻辑而不是去关心 DOM 操作. 目前著名的 MVVM 框架有 vue, avalon, angular 等, 这些框架各有千秋, 但是实现的思想大致上是相同的: 数据绑定 和 视图刷新. 跟 MVC 一样, 主要目的是分离视图 (View) 和模型(Model).View 可以独立于 Model 变化和修改, 一个 ViewModel 可以绑定到不同的 "View" 上, 当 View 变化的时候 Model 可以不变, 当 Model 变化的时候 View 也可以不变.
在 MVVM 中, 数据是核心, 由于 VIewModel 与 View 之间的双向绑定, 操作了 ViewModel 中的数据, 就会同步到 DOM, 我们透过 DOM 事件监控用户对 DOM 的改动, 也会同步到 ViewModel.
MVVM 的缺点
数据绑定使得 Bug 很难被调试. 你看到界面异常了, 有可能是你 View 的代码有 Bug, 也可能是 Model 的代码有问题. 数据绑定使得一个位置的 Bug 被快速传递到别的位置, 要定位原始出问题的地方就变得不那么容易了.
一个大的模块中 model 也会很大, 虽然使用方便了也很容易保证了数据的一致性, 当时长期持有, 不释放内存就造成了花费更多的内存.
数据双向绑定不利于代码重用. 客户端开发最常用的重用是 View, 但是数据双向绑定技术, 让你在一个 View 都绑定了一个 model, 不同模块的 model 都不同. 那就不能简单重用 View 了.
MVP 设计模式
引用的网络图片
1,MVP 模式是 MVC 的一个演化版本, 全称是 Model view Presenter.
2,MVP 能够有效的降低 View 的复杂性, 避免业务逻辑被塞进 View 中, 使得 View 变成一个混乱的 "大泥坑".
3,MVP 模式会解除 View 与 Model 的耦合, 同时又带来了良好的可扩展性, 可测试性, 保证了系统的整洁性.
优点: 降低耦合度, 模块职责划分明显, 利于多人开发, 利于测试驱动开放, 代码服用, 隐藏数据. 缺点: 由于对视图的渲染放在了 Presenter 中, 所以视图和 Presenter 的交互会过于频繁. 如果 Presenter 过多地渲染了视图, 往往会使得它与特定的视图的联系过于紧密. 一旦视图需要变更, 那么 Presenter 也需要变更了.
传统的原生应用开发使用的更多的是 mvc 设计模式; h5, 小程序等更多的是 mvvm 设计模式.
来源: http://www.jianshu.com/p/56b759cca438