目录
(2)简单工厂模式、工厂模式、抽象工厂模式(Factory Method pattern)
(13)责任链模式(Chain-of-responsibility pattern)
(14)模版方法模式(Template method pattern)
一、概述
无论是初出茅庐还是代码老手,平时工作或面试的时候,经常会提到“设计模式”这个词,还记得刚接触iOS还不算入门的时候,第一次给领导汇报工作进度,就把MVC设计模式写在报告里,瞬间感觉自己写的东西好像专业一些了!
后来接触项目越来越多,MVC更是耳熟能详,但随着学习积累,突然发现以前学东西不够系统和全面。那么问题第一个问题来了,MVC是不是设计模式?面试中也经常会问,例如:你经常使用的设计模式有哪些,MVC……无论是别人问我,也或是我问别人,这个答案都几乎没什么毛病。但既然这篇内容打算细聊设计模式,那答案可能不是那么简单的!
平时开发中,也会接触很多设计模式,知道名字的或者不知道的,其实都在用。那么常用的设计模式有哪些?代理模式、单例模式……那么到底有多少种设计模式?下文会详细的聊聊这个话题。
二、框架模式,框架,设计模式
首先搞清楚“框架模式”,“框架”,“设计模式”这三个概念:
框架通常是代码重用,而设计模式是设计重用,架构则介于两者之间,部分代码重用,部分设计重用,有时分析也可重用。有很多程序员往往把框架模式和设计模式混淆,认为MVC是一种设计模式。实际上它们完全是不同的概念。
在软件生产中有三种级别的重用:内部重用,即在同一应用中能公共使用的抽象块;代码重用,即将通用模块组合成库或工具集,以便在多个应用和领域都能使用;应用框架的重用,即为专用领域提供通用的或现成的基础结构,以获得最高级别的重用性。
框架与设计模式虽然相似,但却有着根本的不同。设计模式是对在某种环境中反复出现的问题以及解决该问题的方案的描述,它比框架更抽象;框架可以用代码表示,也能直接执行或复用,而对模式而言只有实例才能用代码表示;设计模式是比框架更小的元素,一个框架中往往含有一个或多个设计模式,框架总是针对某一特定应用领域,但同一模式却可适用于各种应用。可以说,框架是软件,而设计模式是软件的知识。
框架模式有哪些?
MVC、MTV、MVP、CBD、ORM、MVVM等等;
框架有哪些?
C++语言的QT、MFC、gtk,Java语言的SSH 、SSI,php语言的 smarty(MVC模式),python语言的django(MTV模式)等等
设计模式有哪些?
工厂模式、适配器模式、策略模式等等
简而言之:框架是大智慧,用来对软件设计进行分工;设计模式是小技巧,对具体问题提出解决方案,以提高代码复用率,降低耦合度。
如果打个比喻,我会用对象编程思维里的几个产物来比喻设计模式、架构和框架:
一、设计模式就像是接口
抽象得无法再抽象了,基本上设计得有水平的接口,看上去简单,但其实包含了设计者的关系归纳取舍。
二、架构就像是抽象类
因为架构带有不完整的实现、轻量的公共的实现,留有大量容器去给指定的程序员开发
三、框架就像是类
因为框架必须提供完整的功能配套,偶尔留下的一些关系变化,也不是主角,它就像已经可以用的摇控器
那么概述里的第一个问题,MVC是不是设计模式?你还会那么简单的回答吗!!!MVC本质上是一种架构,数据,渲染,控制分离,因为这种架构很优雅,所以设计优雅架构时可以以此作为规则,因此MVC看做设计模式的一种也不算错。相信肯定还有不同的理解和看法,欢迎回复区一起讨论和学习。下面结合实际代码一个个的去了解。
1、框架模式
重点了解MVC、MVVM、MVP
(1)MVC
MVC是model-view-control的简称。
View——顾名思义,就是存放视图使用的。
Model——即模型。模型一般都有很好的可复用性,统一管理一些数据。在上面的例子中,数据库是不是可以作为一个模型呢?答案是肯定的。所以,我们就把数据库的所有操作都放在Model里面执行——包括但不限于数据库的创建、插入、查询、更新和删除
Controller——控制器,充当一个CPU的功能,即该应用程序所有的工作都由Controller统一调控。它负责处理View和Model的事件。具体怎么调控和处理?在下面的MVC原理里面,我们将详细讲解。
MVC模式能够完成各司其职的任务模式,由于降低了各个环节的耦合性,大大优化Controller的代码量,当程序调试时,如果某一个功能没有按照既定的模式工作,可以很方便的定位到到底是Controller还是View还是Model出了问题,而且还利于程序的可复用性。
MVC模式虽然是iOS编程中使用最广泛的模式,但论起复杂程度,MVC模式可以算是众多设计模式之首。通常情况下,MVC模式需要综合使用target-action模式、delegate模式、Notification或KVO模式等。下图是斯坦福大学的iOS一堂关于iOS介绍的公开课上所使用的示例图,这张图像也生动的描绘出来了MVC模式的工作原理,接下来的原理讲解也是依托于这张图像:
1、 Controller和View之间可以通信,Controllor通过outlet(输出口)控制View,View可以通过target-action、delegate或者data source(想想UITableVeiwDatasource)来和Controller通信;
2、 Controller在接收到View传过来的交互事件(View就是完成让人和程序的交互的呀,比如按B1按钮)之后,经过一些判断和处理,把需要Model处理的事件递交给Model处理(比如刚才的例子中的保存到数据库),Controller对Model使用的是API;
3、 Model在处理完数据之后,如果有需要,会通过Notification或者KVO的方式告知Controller,事件已经处理完,Controller再经过判断和处理之后,考虑下一步要怎么办(是默默无闻的在后台操作,还是需要更新View,这得看Controller的处理)。这里的无线天线很有意思,Model只负责发送通知,具体谁接收这个通知并处理它,Model并不关心,这一点非常重要,是理解Notification模式的关键。
4、 Model和View之间不直接通信!
按照上面的原理,我们知道了M、V、C之间的各司其职——Model不保存控件,View不做数据库操作(但这个也不是绝对,如果需要View做一些数据缓存工作,还是需要保存一些临时数据的),而Controller就充当了两者之间的协调器。
如此,MVC的原理已经理出来一个头绪了,那么我们来看一个实际的例子,来验证如何使用MVC模式。在这个例子中,View通过target-action模式向Controller传递消息,Controller通过API调用Model里面的方法来处理从View那接收到的消息;Model处理完数据之后,通过Notification模式向Controller传递一个消息,最终Controller通过一个方法(即Notification的接收方法)弹出来一个对话框显示Model已经处理完成。
MVC小总结:
M应该做的事:
1.给ViewController提供数据
2.给ViewController存储数据提供接口
3.提供经过抽象的业务基本组件,供Controller调度
C应该做的事:
1.管理View Container的生命周期
2.负责生成所有的View实例,并放入View Container
3.监听来自View与业务有关的事件,通过与Model的合作,来完成对应事件的业务。
V应该做的事:
1.响应与业务无关的事件,并因此引发动画效果,点击反馈(如果合适的话,尽量还是放在View去做)等。
2.界面元素表达
(2)MVVM
上边聊完了MVC,但在实际开发中,M和V的职责很清晰了,那剩下来的任务都交给C来做了,可能包括V的生命周期代理方法,网络请求,处理各种代理,数据解析和封装等等等,随着项目增大,C会变得很臃肿,不利于维护管理以及BUG定位,于是就出现了改进版本的MVVM。
其中MVVM的M和V同MVC的M和V,但又有区别,那VM又是什么?看下边原理图:
MVVM把View和Contrller都放在了View层(相当于把Controller一部分逻辑抽离了出来),Model层依然是服务端返回的数据模型。而VM,也就是ViewModel充当了一个UI适配器的角色,也就是说View中每个UI元素都应该在ViewModel找到与之对应的属性。除此之外,从Controller抽离出来的与UI有关的逻辑都放在了ViewModel中,这样就减轻了Controller的负担。
- View层:视图展示。包含UIView以及UIViewController,View层是可以持有ViewModel的。
- ViewModel层:视图适配器。暴露属性与View元素显示内容或者元素状态一一对应。一般情况下ViewModel暴露的属性建议是readOnly的,至于为什么,我们在实战中会去解释。还有一点,ViewModel层是可以持有Model的。
- Model层:数据模型与持久化抽象模型。数据模型很好理解,就是从服务器拉回来的JSON数据。而持久化抽象模型暂时放在Model层,是因为MVVM诞生之初就没有对这块进行很细致的描述。按照经验,我们通常把数据库、文件操作封装成Model,并对外提供操作接口。(有些公司把数据存取操作单拎出来一层,称之为DataAdapter层,所以在业内会有很多MVVM的变种,但其本质上都是MVVM)。
- Binder:MVVM的灵魂。可惜在MVVM这几个英文单词中并没有它的一席之地,它的最主要作用是在View和ViewModel之间做了双向数据绑定。如果MVVM没有Binder,那么它与MVC的差异不是很大。
我们发现,正是因为View、ViewModel以及Model间的清晰的持有关系,所以在三个模块间的数据流转有了很好的控制。
那么双向数据绑定如何实现?
随着MVVM问世后,随之伴生而来的就是RAC(ReactiveCocoa),RAC是函数式+响应式编程结合,首先得去理解何为响应式函数编程(FRP)。
看了许多介绍,举一个最通俗易懂的例子——在命令式编程环境中, a = b+c表示将表达式的结果赋给 a,而之后改变 b 或 c的值不会影响 a。但在响应式编程中,a的值会随着 b或 c的更新而更新,意味着声明了一种绑定关系,b、c的变化会直接影响到a。
之前在iOS工作中,类之间的传值,无非就是block、delegate代理、KVO、notification这几种方法。在RAC中,同样具备替代KVO、delegate代理、通知、UI target、计时器timer、数据结构等各种方法。依据响应式函数编程,RAC方法本身更加简洁明了,通过提供信号的方式(RACSignal)可以捕捉当前以及未来的属性值变化,而且无需持续观察和更新代码。可直接在block中将逻辑代码加入其中,使得代码紧凑,更加直观。
虽然RAC功能强大,使得MVVM实现起来更优雅,但成也RAC,败也RAC,因为RAC造成了学习成本高和DEBUG困难,使得一些想要尝试MVVM的人望而却步。
但要纠正两点:
- MVVM绝不等于RAC,所以MVVM并不存在DEBUG难的问题。
- MVVM正是因为跟RAC不对等,所以博文中“MVVM一个首要的缺点是,MVVM的学习成本和开发成本都很高”这句话也是不成立的。
MVVM架构本身并不复杂,而且不用RAC我们依然可以通过KVO、类KVO的方式来帮我们实现View和ViewModel绑定器功能。
(3)MVP
MVP是单词Model View Presenter的首字母的缩写,分别表示数据层、视图层、发布层,它是MVC架构的一种演变。作为一种新的模式,MVP与MVC有着一个重大的区别:在MVP中View并不直接使用Model,它们之间的通信是通过Presenter (MVC中的Controller)来进行的,所有的交互都发生在Presenter内部,而在MVC中View会直接从Model中读取数据而不是通过 Controller。
下面是原理图:
1、MVP分离了view和model层,Presenter层充当了桥梁的角色,View层只负责更新界面即可,这里的View我们要明白只是一个viewinterface,它是视图的接口,这样我们在做单元测试的时候可以非常方便编写Presenter层代码。
2、厚重的Controller层代码也得到了释放,之前我们开发的时候会对UIViewController、Activity、Fragment编写很多的业务逻辑,尽管大家会将Service层做分离,如net层,DB层等,但还是无法避免类似的问题,activity uicontroller无法重复利用是非常难以忍受的。
3、有一点还需要注意,presenter是双向绑定的关系,因此,在设计的时候就要注意接口和抽象的使用,尽可能的降低代码的耦合度,这也是mvp的宗旨。
2、设计模式
平时开发中,设计模式无处不在,只是没感觉到而已,那么iOS开发常用的设计模式有哪些?
原型模式(Prototype pattern)
简单工厂模式、工厂模式、抽象工厂模式(Factory Method pattern)
建造者模式(Builder Pattern)
单例模式(Singleton Pattern)
适配器模式(Adapter Pattern)
中介者模式(Mediator Pattern)
观察者模式(Observer Pattern)
组合模式(Composite Pattern)
访问者模式(Visitor Pattern)
装饰模式(Decorator pattern)
责任链模式(Chain-of-responsibility pattern)
模版方法模式(Template method pattern)
策略模式(Strategy pattern)
命令模式(Command Pattern)
享元模式(Flyweight Pattern)
代理模式(Proxy pattern)
备忘录模式(Memento Pattern)
桥接模式(Bridge Pattern)
外观模式(Facade pattern)
头瞬间大了三号,不过不用着急,这些设计模式在平时开发中,已经接触并使用过了,只是没有归结某种设计模式而已。
(1)原型模式(Prototype pattern)
NSArray *array = [[NSArray alloc] initWithObjects:@1, nil];
NSArray *arrayCopy = [array copy];
实际开发中,这样的代码肯定没少写。原型模式是创建型模式的一种,其特点在于通过「复制」一个已经存在的实例来返回新的实例,而不是新建实例。被复制的实例就是我们所称的「原型」,这个原型是可定制的。
原型模式多用于创建复杂的或者耗时的实例,因为这种情况下,复制一个已经存在的实例使程序运行更高效;或者创建值相等,只是命名不一样的同类数据。
(2)简单工厂模式、工厂模式、抽象工厂模式(Factory Method pattern)
1>简单工厂模式
把很容易变化的地方用一个单独的类来做这个创造实例的过程,这个就是工厂。
看一个计算器的例子:
Operation operationAdd = ^(int a, int b){
return a + b;
};
Operation operationSub = ^(int a, int b){
return a - b;
};
Operation operationMul = ^(int a, int b){
return a * b;
};
Operation operationDiv= ^(int a, int b){
return a / b;
};
+(Operation) createOperate:(NSInteger)operate {
Operation oper = nil;
switch (operate) {
case 0:
oper = operationAdd;
break;
case 1:
oper = operationSub;
break;
case 2:
oper = operationMul;
break;
case 3:
oper = operationDiv;
break;
default:
break;
}
return oper;
}
//使用
Operation oper = nil;
oper = [OperationFactory createOperate:0];
int result = oper(1, 2);
NSLog(@"%d", result);
2>工厂模式
//定义汽车类
@interface Car : NSObject
+(Car *) produce;
@end
//定义特斯拉汽车类
@interface Tesla : Car
@end
@implementation
+(Car *) produce {
return [Tesla new];
}
@end
//定义奥迪汽车类
@interface Audi : Car
@end
@implementation
+(Car *) produce {
return [Audi new];
}
@end
“工厂方法模式(Factory Method Pattern)又称为工厂模式,也叫虚拟构造器(Virtual Constructor)模式或者多态工厂(Polymorphic Factory)模式,它属于类创建型模式。在工厂方法模式中,工厂父类负责定义创建产品对象的公共接口,而工厂子类则负责生成具体的产品对象,这样做的目的是将产品类的实例化操作延迟到工厂子类中完成,即通过工厂子类来确定究竟应该实例化哪一个具体产品类。
上述代码中,特斯拉(Tesla)和奥迪(Audi)都继承了汽车类(Car),其子类Tesla和Audi都拥有了produce方法,但其produce方法生产汽车的特性,是由各汽车厂家决定的。
再看一个例子:
在 Cocoa Touch 框架中,以 NSArray 举例,将原有的 alloc+init (相当于不同对象调用 new 方法) 拆开写:
id obj1 = [NSArray alloc]; // __NSPla