什么是设计模式?
百度百科上是这样定义的:设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。
对于软件开发中的设计模式,最早作出权威性论述和分类的是《设计模式》(Erich Gamma、Richard Helm、Ralph Johnson和John Vlissides著),根据《设计模式》一书的描述,设计模式是对于定制来解决特定场景下设计的类和相互通信的对象的描述。简而言之,设计模式是为特定场景下的问题而定制的解决方案。而程序设计中使用设计模式,对代码的复用和扩展及后续的变更都更加容易,而且程序也会变得更加简洁和高效。下面是开发中常用到的几种设计模式的解析及在iOS开发中的应用。
1、单例模式
单例模式几乎是设计模式中最简单的一种设计形式了,这一模式的作用是使得类的一个对象成为系统中的唯一实例。要实现这一点,可以从客户端对其进行实例化开始,需要用一种只允许生成对象的唯一实例的机制。
在iOS开发中使用单例模式
iOS的开发框架(Cocoa Touch)中使用单例模式最典型的例子是UIApplication、UIAccelerometer和NSFileManager。
UIApplication类:提供了一个控制并协调iOS应用程序的集中点,每个应用程序有且仅有一个UIApplecation的实例,它由UIApplicationMain函数在应用程序启动时创建为单例对象,之后对同一UIApplication实例可以通过其sharedApplication类方法进行访问。
UIAccelerometer类:UIAccelerometer类让应用程序可以进行注册,以接收来自iOS设备内置的加速度计相关的数据。
NSFileManager类:通过defaultMannger类方法进行创建和访问。
2、工厂方法
工厂方法是面向对象软件设计中应用非常普遍的设计模式,工厂方法从代码中消除了对应用程序特有类的耦合,代码只需处理Product抽象接口。它适用于这种情况:一个类无法预期需要生成哪个类的对象,想让其子类来指定所生成的对象。
在iOS开发中使用工厂方法
在Cocoa Touch框架中几乎随处可见工厂方法,例如,NSNumber的很多numberWith * 方法[NSNumber
numberWithBool:bool],[NSNumber numberWithChar:char]等,
3、原型模式(对象复制)
原型模式是一种非常简单的设计模式,如客户端知道抽象类ObjectClass类,在运行时,抽象ObjectClass子类的任何对象可以按需求被复制,因此,无需手工创建就可以制造同一类型的多个实例。
在iOS开发中使用原型模式
Cocoa Touch框架中为NSObject的类提供了实现深复制的协议。NSObject的子类需要实现NSCopying协议及其方法--(id)copyWithZone:(NSZone*)zone。NSObejct有一个实例方法(id)copy。默认的copy方法调用[self copyWithZone:nil]。对于采纳了NSCopying协议的子类,需要实现这个方法,否则会引发异常。iOS中,这个方法保持新的副本对象,然后将其返回。此方法的调用者要负责释放返回的对象。
4、中介者模式
面向对象的设计鼓励吧行为分散到不同对象中,这种分散可能导致对象之间的相互关联,且所有对象都彼此了解并互相操作。虽然吧行为分散到不同对象增强了可复用性,但是增加的相互关联使得对象很难在不依赖其他对象的情况下工作,应用程序的整体也难以进行任何重大的修改,因为行为分布于许多对象。于是结果可能是创建越来越多的子类,以支持应用程序的新行为。
中介模式就可以解决此类问题,对象间的交互可以在一个中介者对象中处理,其他对象不必彼此交互,减少了它们之间的依存关系。
虽然对于处理应用程序的行为分散于不同对象并且对象互相依存的情况,中介者模式非常有用,但是应当注意避免让中介者类过于庞大而难以维护。如果这样,可以考虑使用下面的这一种“发布-订阅”模式来消除对象耦合。
5、‘发布-订阅’(观察者)模式
发布-订阅模式就像杂志的订阅一样,Observer从Subject订阅通ConcreteObserver实现抽象Observer并重载其update方法。一旦Subject的实例需要通知Observer任何新的变更,Subject会发送update消息来通知存储在内部列表中所有注册的Observer。
‘发布-订阅’模式在以下几种情况下比较适用:
有两种抽象类型相互依赖,将他们封装在各自的对象中,就可以对它们单独进行改变和复用;对一个对象的改变需要同时改变其他对象,而不知道具体有多少对象有待改变;一个对象必须通知其他对象,而它又不需知道其他对象是什么。
在iOS开发中使用‘发布-订阅’模式
在iOS框架中用两种技术改写了‘发布-订阅’模式---通知和键值观察(Key-Value Observing)。
通知:Cocoa Touch框架使用NSNotificationCenter和NSNotification对象实现了一对多的发布-订阅模式,它们允许主题与观察者以一种松耦合的方式通信,两者在通信时对另一方无需多少了解决。
键-值观察:对象可以通过它得到其他对象特定属性的变更通知。这种机制在MVC(模型-视图-控制器)模式的场景中尤其重要,因为它让视图对象可以经由控制器层观察模型对象的变更。这一机制基于NSKeyValueObserving非正式协议,Cocoa通过这个协议为所有遵守协议的对象提供了一种自动化的属性观察能力。要实现自动观察,参与键-值观察的对象需要符合键值编码(KVC)的要求,并且需要符合KVC的存取方法。
6、组合模式
组合模式让我们可以把相同基类型(base type)的对象组合到树状结构中,其中的父节点包含同类型的子节点。换句话说,这种树状结构形成‘部分-整体’的层次结构。什么是‘部分-整体’的层次结构呢?它是既包含对象的组合(容器)又包含作为叶节点的单个对象的一种层次结构。这种关系在这个层次结构中递归重复。
在iOS开发中使用组合模式
在Cocoa Touch框架中,UIView被组织成一个组合结构,每个UIView的实例可以包含UIView的其他实例,形成统一的树形结构,让客户端对 单个UIView对象和UIView的组合统一对待。层次结构的根部是一个UIWindow对象和它的内容视图。添加进来的其他UIView成为它的子视图。UIView对象只能有一个超视图,可以有多个子视图。
组合模式的主要意图是让树形结构中的每个节点具有相同的抽象接口,这样整个结构可作为一个统一的抽象结构使用,而不暴露其内部表示。对每个节点的任何操作,可以通过协议或抽象基类中定义的相同接口来进行。
7、迭代器模式
要迭代器模式提供了一种顺序访问聚会对象中元素的方法,而无需暴露结构底层表示和细节。遍历集合中元素的职能从集合本身转移到迭代器对象,迭代器定义了一个用于访问集合元素并记录当前元素的接口。不同的迭代器可以执行不同的遍历策略。
在iOS开发中使用迭代器模式
iOS基础框架中的NSEnumerator类实现了迭代器模式,抽象NSEnumerator类的私有具体子类返回枚举器对象,能够顺序遍历各种集合—数组、集(set)、字典(值与键),把集合中的对象返回给客户端。
NSDirectoryEnumerator是个关系较远的类。这个类的实例递归枚举文件系统中一个目录的内容。
NSArray、NSSet和NSDictionary这样的集合类,定义了返回与集合的类型相应的NSEnumerator子类实例的方法。所有的枚举器都以同样的方式工作。可以在一个循环中向枚举器发送nextObject消息,从枚举器取得对象,直到它返回nil表示遍历结束。
8、备忘录模式
在响应某些事件时,应用程序需要保存自身的状态,比如当用户登录后退出时保存用户的用户名或者游戏退出之前,可能需要保存当前会话的状态等。很多时候,保存程序的状态真的不需要什么特别奇妙的方法,任何简单有效的方法都可以。这就是备忘录模式应用于游戏、文字处理等程序的软件设计方式,这些程序需要保存当前上下文的复杂状态的快照并在以后恢复。
在iOS开发中使用备忘录模式
保存Cocoa Touch框架在归档、属性列表系列化和核心数据中都采用了备忘录模式。Cocoa的归档是对对象及其属性还有同其他对象间的关系进行编码,形成一个文档,该文档既可保存于文件系统,也可在进程或网络间传送。
归档通过使用NSCoder对象进行编码和解码操作,NSCoder本身是个抽象类,苹果公司建议通过NSCoder的具体类NSKeydArchiver和NSKeyedUnarchiver,使用基于键的归档技术。被编码与解码的对象须遵守NSCoding协议并实现以下方法:
-(id)initWithCoder:(NScoder)coder;
-(id)encodeWithCoder:(NSCoder)coder;
这两个方法是归档和解档过程的关键。
总结
本文简述了几种常用的设计模式并列举了他们在iOS实际开发中的使用场景,这些设计模式在我们平时的项目开发过程中有些可作为单独的解决方案来使用,有些则是合并为一组复合模式使用,总体来说都是前人总结下来的比较宝贵的开发经验。在我们平时的编码过程中,可能在一定程度上也运用了一些设计模式,只是并没有意识到或充分利用它们,而如果我们对这些设计模式有了比较深入的了解,并能在我们平时的编码中加以应用,那我们编出的代码一定会非常优雅和简洁。好的设计模式一定会让开发工作变得更加高效。