设计模式
创建型模式
- 工厂模式:定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类,工厂模式使其创建过程延迟到子类进行。
- 抽象工厂模式:提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
- 单例模式:保证一个类仅有一个实例,并提供一个访问它的全局访问点。
- 建造者模式:将一个复杂的构建与其表示相分离,使得同样的构建过程可以创建不同的表示。
- 原型模式:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
结构型模式
- 适配器模式:将一个类的接口转换成客户希望的另外一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
- 桥接模式:将抽象部分与实现部分分离,使它们都可以独立的变化。
- 过滤器模式:这种模式允许开发人员使用不同的标准来过滤一组对象,通过逻辑运算以解耦的方式把它们连接起来
- 组合模式:将对象组合成树形结构以表示"部分-整体"的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。
- 装饰器模式:动态地给一个对象添加一些额外的职责。就增加功能来说,装饰器模式相比生成子类更为灵活。
- 外观模式:为子系统中的一组接口提供一个一致的界面,外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。
- 享元模式:运用共享技术有效地支持大量细粒度的对象。
- 代理模式:为其他对象提供一种代理以控制对这个对象的访问。
行为型模式
- 责任链模式:避免请求发送者与接收者耦合在一起,让多个对象都有可能接收请求,将这些对象连接成一条链,并且沿着这条链传递请求,直到有对象处理它为止。
- 命令模式:将一个请求封装成一个对象,从而使您可以用不同的请求对客户进行参数化。
- 解释器模式:给定一个语言,定义它的文法表示,并定义一个解释器,这个解释器使用该标识来解释语言中的句子。
- 迭代器模式:提供一种方法顺序访问一个聚合对象中各个元素, 而又无须暴露该对象的内部表示。
- 中介者模式:用一个中介对象来封装一系列的对象交互,中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。
- 备忘录模式:在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。
- 状态模式:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
- 空对象模式:在空对象模式中,我们创建一个指定各种要执行的操作的抽象类和扩展该类的实体类,还创建一个未对该类做任何实现的空对象类,该空对象类将无缝地使用在需要检查空值的地方。
- 策略模式:定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换。
- 模板模式:定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
- 访问者模式:主要将数据结构与数据操作分离。
面向对象设计七大原则
- 单一职责原则(Single Responsibility Principle)
每一个类应该专注于做一件事情。
- 里氏替换原则(Liskov Substitution Principle)
超类存在的地方,子类是可以替换的。
- 依赖倒置原则(Dependence Inversion Principle)
实现尽量依赖抽象,不依赖具体实现。
- 接口隔离原则(Interface Segregation Principle)
应当为客户端提供尽可能小的单独的接口,而不是提供大的总的接口。
- 迪米特法则(Law Of Demeter)
又叫最少知识原则,一个软件实体应当尽可能少的与其他实体发生相互作用。
- 开闭原则(Open Close Principle)
面向扩展开放,面向修改关闭。
- 组合/聚合复用原则(Composite/Aggregate Reuse Principle CARP)
尽量使用合成/聚合达到复用,尽量少用继承。原则: 一个类中有另一个类的对象。
尾递归:
如果一个函数中所有递归形式的调用都出现在函数的末尾,我们称这个递归函数是尾递归的。当递归调用是整个函数体中最后执行的语句且它的返回值不属于表达式的一部分时,这个递归调用就是尾递归。
精髓就是通过参数传递结果,达到不压栈的目的。当编译器检测到一个函数调用是尾递归的时候,它就覆盖当前的活动记录而不是在栈中去创建一个新的。
参考:递归和尾递归的区别和原理
facttail(int n, int res)
{
if (n < 0)
return 0;
else if (n == 0)
return 1;
else if (n == 1)
return res;
else
return facttail(n - 1, n * res);
}
MVC与MVVM
-
标准MVC
然而由于一般View和Controller都是成对出现,View和Controller总是表现得像是同一层,一般实际的模式是M-VC
-
MVC实际上是M-VC
MVC的问题
1. 厚重的View Controller
- 模型model的对象通常非常的简单
- View只有界面没有逻辑代码,也不引用Model(实际编码一般不是这样)
- Controller是app的“胶水代码”:协调模型和视图之间的所有交互
网络数据的请求及后续处理,本地数据库操作,以及一些带有工具性质辅助方法都加大了Massive View Controller的产生。
2. 无处安放的网络逻辑
- 网络逻辑不能放在Model也不能放到View,一般只能放到Controller,这又加剧了Massive View Controller的问题
3. 较差的可测试性
- 由于View Controller混合了视图处理逻辑和业务逻辑,分离这些成分的单元测试成了一个艰巨的任务。
MVVM
在MVVM 中,view 和 view controller正式联系在一起,我们把它们视为一个组件
view 和 view controller 都不能直接引用model,而是引用视图模型(viewModel)
viewModel 是一个放置用户输入验证逻辑,视图显示逻辑,发起网络请求和其他代码的地方
使用MVVM会轻微的增加代码量,但总体上减少了代码的复杂性
view 引用viewModel ,但反过来不行(即不要在viewModel中引入#import UIKit.h,任何视图本身的引用都不应该放在viewModel中)
viewModel 引用model,但反过来不行
MVVM 的使用建议
- MVVM 可以兼容你当下使用的MVC架构。
- MVVM 增加你的应用的可测试性。
- MVVM 配合一个绑定机制效果最好(PS:ReactiveCocoa你值得拥有)。
- viewController 尽量不涉及业务逻辑,让 viewModel 去做这些事情。
- viewController 只是一个中间人,接收 view 的事件、调用 viewModel 的方法、 响应 viewModel 的变化。
- viewModel 绝对不能包含视图 view(UIKit.h),不然就跟 view 产生了耦合,不方便复用和测试。
- viewModel之间可以有依赖。
- viewModel避免过于臃肿,否则重蹈Controller的覆辙,变得难以维护。
MVVM 的优势
- 低耦合:View 可以独立于Model变化和修改,一个 viewModel 可以绑定到不同的 View 上
- 可重用性:可以把一些视图逻辑放在一个 viewModel里面,让很多 view 重用这段视图逻辑
- 独立开发:开发人员可以专注于业务逻辑和数据的开发 viewModel,设计人员可以专注于页面设计
- 可测试:通常界面是比较难于测试的,而 MVVM 模式可以针对 viewModel来进行测试
MVVM 的弊端
数据绑定使得Bug 很难被调试。你看到界面异常了,有可能是你 View 的代码有 Bug,也可能是 Model 的代码有问题。数据绑定使得一个位置的 Bug 被快速传递到别的位置,要定位原始出问题的地方就变得不那么容易了。
对于过大的项目,数据绑定和数据转化需要花费更多的内存(成本)。
主要成本在于:
数组内容的转化成本较高:数组里面每项都要转化成Item对象,如果Item对象中还有类似数组,就很头疼。
转化之后的数据在大部分情况是不能直接被展示的,为了能够被展示,还需要第二次转化。只有在API返回的数据高度标准化时,这些对象原型(Item)的可复用程度才高,否则容易出现类型爆炸,提高维护成本。
调试时通过对象原型查看数据内容不如直接通过NSDictionary/NSArray直观。
同一API的数据被不同View展示时,难以控制数据转化的代码,它们有可能会散落在任何需要的地方。
函数式编程
函数式编程是一种将程序看成是数学方法的求值、不会改变状态、不会产生副作用的编程方式。
- 声明式代码 —— 程序员应该关心是什么,让编译器和运行环境去关心怎样做。
- 明确性 —— 代码应该尽可能的明显。尤其是要隔离副作用避免意外。要明确定义数据流和错误处理,要避免 GOTO 语句和 异常,因为它们会将应用置于意外的状态。
- 并发 —— 因为纯函数的概念,大多数函数式代码默认都是并行的。
- 高阶函数 —— 函数和其他的语言基本元素一样是一等公民。你可以像使用 string 和 int 一样的去传递函数。
- 不变性 —— 变量一经初始化将不能修改。一经创建,永不改变。
响应式编程
响应式编程是一种通过异步和数据流来构建事物关系的编程模型。
- 在业务层面实现代码逻辑分离,方便后期维护和拓展
- 极大提高程序响应速度,充分发掘CPU的能力
- 帮助开发者提高代码的抽象能力和充分理解业务逻辑
- 丰富的操作符会帮助我们极大的简化代码逻辑