iOS设计模式学习实例

来源:互联网 发布:淘宝网站的盈利模式 编辑:程序博客网 时间:2024/05/22 01:49

源码下载(已添加详细注释):http://download.csdn.net/detail/u013088635/7304793

本应用实例中包含大部分的cocoa设计模式:

创建型:单例(单态)和抽象工厂

结构型:MVC、装饰器、适配器、外观(门面)、组合模式

行为型:观察者、备忘录、责任链、命令模式

1、MVC

确保在你工程中的每个类是控制器,模型和视图中的一种,不要在一个类中组合两种角色的功能。涉及到的三个角色如下:
Model:模型保存应用程序的数据,定义了怎么去操作它。例如在本应用中模型就是Album类。
View:视图是模型的可视化表示以及用户交互的控件;基本上来说,所有的UIView对象以及它的子类都属于视图。在本应用中AlbumView代表了视图。
Controller:控制器是一个协调所有工作的中介者(Mediator)。它访问模型中的数据并在视图中展示它们,同时它们还监听事件和根据需要操作数据。

2、单例(单态)模式

单例设计模式确保对于一个给定的类只有一个实例存在,这个实例有一个全局唯一的访问点。它通常采用懒加载的方式在第一次用到实例的时候再去创建它。
注意:苹果大量使用了此模式。例如:[NSUserDefaults standardUserDefaults], [UIApplication sharedApplication], [UIScreen mainScreen], [NSFileManager defaultManager],所有的这些方法都返回一个单例对象。

3、门面(Facade)模式

门面模式针对复杂的子系统提供了单一的接口,不需要暴漏一些列的类和API给用户,你仅仅暴漏一个简单统一的API。

在本实例中,当前你已经用PersistencyManager本地保存专辑数据,使用HTTPClient处理远程连接,工程中的其它类暂时与本次实现的逻辑无关。
为了实现这个模式,只有LibraryAPI应该保存PersistencyManager和HTTPClient的实例,然后LibraryAPI将暴漏一个简单的API去访问这些服务。
 
注意: 通常来说,单例类的生命周期贯穿于整个应用的生命周期中,你不应对保存太多其它对象的强引用,因为他们只有到应用关闭的时候才能被释放。

4、装饰器(Decorator)模式

装饰器模式在不修改原来代码的情况下动态的给对象增加新的行为和职责,它通过一个对象包装被装饰对象的方法来修改类的行为,这种方法可以做为子类化的一种替代方法。
在Objective-C中,存在两种非常常见的实现:Category(类别)和Delegation(委托)

5、适配器(Adapter)模式

适配器可以让一些接口不兼容的类一起工作。它包装一个对象然后暴漏一个标准的交互接口。
如果你熟悉适配器设计模式,苹果通过一个稍微不同的方式来实现它-苹果使用了协议的方式来实现。你可能已经熟悉UITableViewDelegate, UIScrollViewDelegate, NSCoding 和 NSCopying协议。举个例子,使用NSCopying协议,任何类都可以提供一个标准的copy方法。

6、观察者(Observer)模式

在观察者模式中,一个对象任何状态的变更都会通知另外的对改变感兴趣的对象。这些对象之间不需要知道彼此的存在,这其实是一种松耦合的设计。当某个属性变化的时候,我们通常使用这个模式去通知其它对象。
Cocoa通过通知(Notifications)和Key-Value Observing(KVO)来实现观察者模式。

6.1、如何使用通知(Notifications)

打开AlbumView.m,在initWithFrame:albumCover::方法的[self addSubview:indicator];语句之后加入如下代码:

//观察者模式中通过NSNotificationCenter单例发送了一个通知        [[NSNotificationCenter defaultCenter] postNotificationName:DOWNLOAD_NOTIFICATON object:self userInfo:@{@"imageView":coverImage, @"coverUrl":albumCover}];

NotificationCenter单例发送了一个通知。这个通知包含了UIImageView和需要下载的封面URL,这些是你下载任务所需要的所有信息。

 
在LibraryAPI.m文件init方法的isOnline=NO之后,增加如下的代码:
//这个是观察者模式中两部分的另外一部分:观察者。每次AlbumView发送一个BLDownloadImageNotification通知,因为LibraryAPI已经注册为同样的通知的观察者,那么系统就会通知LibraryAPI,LibraryAPI又会调用downloadImage:来响应。        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(downloadImage:) name:DOWNLOAD_NOTIFICATON object:nil];

这个是观察者模式中两部分的另外一部分:观察者。每次AlbumView发送一个BLDownloadImageNotification通知,因为LibraryAPI已经注册为同样的通知的观察者,那么系统就会通知LibraryAPI,LibraryAPI又会调用downloadImage:来响应。
然而在你实现downloadImage:方法之前,你必须在你的对象销毁的时候,退订所有之前订阅的通知。如果你不能正确的退订的话,一个通知发送给一个已经销毁的对象会导致你的app崩溃。
在Library.m中增加下面的代码:
- (void)dealloc   {       [[NSNotificationCenterdefaultCenter] removeObserver:self];      } 

当对象被销毁的时候,它将移除所有监听通知的观察者。

6.2、Key-Value Observing(KVO)模式

 在KVO中,一个对象可以要求在它自身或者其它对象的属性发送变化的时候得到通知。
正如上面所说的,KVO机制让对象可以感知到属性的变化。在本例中,你可以使用KVO去观察UIImageView的image属性的变化。
[coverImage addObserver:self forKeyPath:@"image" options:0 context:nil];
这里它增加了它自己(当前的类)作为image属性的观察者。
当完成的时候,你同样需要注销相应的观察者:
- (void)dealloc   {       [coverImage removeObserver:self forKeyPath:@"image"];      } 
最后增加下面的方法:
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {    if ([keyPath isEqualToString:@"image"]) {        NSLog(@"change happen, old:%@   new:%@",[change objectForKey:NSKeyValueChangeOldKey],[change objectForKey:NSKeyValueChangeNewKey]);         [indicator stopAnimating];    }}

你必须在每个观察者类中实现这个方法。系统会在被观察的属性发送变化的时候通知观察者。在上面的代码中,当image属性变化的时候,你停止了封面上面的旋转提示器。这样以来,当图片加载完后,旋转提示器将会停止。
KVC KVO KVB详细讲解

7、备忘录(Memento)模式

备忘录模式快照对象的内部状态并将其保存到外部。换句话说,它将状态保存到某处,过会你可以不破坏封装的情况下恢复对象的状态,也就是说原来对象中的私有数据仍然是私有的。

8、归档(Archiving)

归档是苹果对于备忘录模式的特定实现之一。这种机制可以转换一个对象到一个可保存的数据流中,过会可以在不暴漏私有属性给外部的情况下重建它们。遵循NSCoding协议

9、命令模式

命令模式将一个请求封装为一个对象。封装以后的请求会比原生的请求更加灵活,因为这些封装后的请求可以在多个对象之间传递,存储以便以后使用,还可以动态的修改,或者放进一个队列中。苹果通过Target-Action机制和Invocation实现命令模式。

- (void)deleteAlbum {    //1 获取需要删除的专辑    Album *deletedAlbum = allAlbums[currentAlbumIndex];        //2 定义了一个类型为NSMethodSignature的对象去创建NSInvocation,它将用来撤销删除操作。NSInvocation需要知道三件事情:选择器(发送什么消息),目标对象(发送消息的对象),还有就是消息所需要的参数。在上面的例子中,消息是与删除方法相反的操作,因为当你想撤销删除的时候,你需要将刚删除的数据回加回去。    NSMethodSignature *sig = [self methodSignatureForSelector:@selector(addAlbum:atIndex:)]; //获得类和方法的签名    NSInvocation *undoAction = [NSInvocation invocationWithMethodSignature:sig]; //从签名获得调用对象    [undoAction setTarget:self]; //设置target    [undoAction setSelector:@selector(addAlbum:atIndex:)];//设置selector    [undoAction setArgument:&deletedAlbum atIndex:2];//设置参数,第一个参数index为2    [undoAction setArgument:¤tAlbumIndex atIndex:3];//设置参数,第二个参数index为3    [undoAction retainArguments]; //retain一遍参数        //3 创建了undoAction以后,你需要将其增加到undoStack中。撤销操作将被增加在数组的末尾。    [undoStack addObject:undoAction];        //4 使用LibraryAPI删除专辑,然后重新加载滚动视图。    [[LibraryAPI sharedInstance] deleteAlbumAtIndex:currentAlbumIndex];    [self reloadScroller];        //5 因为在撤销栈中已经有了操作,你需要使得撤销按钮可用。    [toolbar.items[0] setEnabled:YES];}


注意:使用NSInvocation,你需要记住下面的几点:
1.参数必须以指针的形式传递.
2.参数从索引2开始,索引0,1为目标(target)和选择器(selector)保留。
3.如果参数有可能会被销毁,你需要调用retainArguments.

原文地址:http://www.cocoachina.com/newbie/basic/2013/0917/7009.html


0 0