命令模式
这个设计模式将对象封装成了一个请求获取操作,这个封装请求比一个原始的请求更加的灵活,且可以在对象之间传递,稍后存储,动态修改获取放到一个队
列之中。苹果公司是用Target-action机制和Invocation实现的,你可以在苹果的官方文档中去知道更多的关于Target-
Action,但是Invocation使用包含一个Target对象的NSInvocation类,一个方法选择器和一些参数。这个对象可以根据需要动
态的改变和执行,在命令模式中这是一个完美的例子,它解除耦合了发送对象和接收对象,且可以持续的坚持一个或者一系列请求。
如何使用命令模式:
早莫深入动作的invocation之前,你需要去设置撤销(undo action)动作的框架,所以你必须定义一个UIToolBar 和undo stack(撤销栈)上所需要的可变数组。
在ViewController.m的扩展中键入这些代码:
UIToolbar *toolBar; tableArray *undoStack;
这创建了一个为了这些操作而添加显示的toolbar,还要一个数组去去作为命令队列。
添加下面的代码到ViewDidLoad的结尾:
toolBar = [[UIToolbaralloc]init]; UIBarButtonItem *undoBtm= [[UIBarButtonItemalloc]initWithBarButtonSystemItem:UIBarButtonSystemItemUndotarget:selfaction:@selector(undoAction)]; undoBtm.enabled = NO; UIBarButtonItem *space =[[UIBarButtonItemalloc]initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpacetarget:nilaction:nil]; UIBarButtonItem *delete =[[UIBarButtonItemalloc]initWithBarButtonSystemItem:UIBarButtonSystemItemTrash target:selfaction:@selector(deleteAction)]; [toolBarsetItems:@[undoBtm,space,delete]]; [self.viewaddSubview:toolBar]; undoStack = [[NSMutableArrayalloc]init];
上面的代码创建了一个toolbar,它有两个按钮和一个可变的空格。这个撤销按钮被禁止了因为这个undo stack一开始是空的。
同样,因为这个toolbar没有根据frame来初始化,所以这个在viewDidload中frame的大小还没有设置。所以通过下面的一些代码在一旦视图的frame被最终设置好了以后设置这个frame。
-(void)viewWillLayoutSubviews { toolBar.frame = CGRectMake(0, self.view.frame.size.height-44, self.view.frame.size.width,44); dataTable.frame = CGRectMake(0, 130, self.view.frame.size.width, self.view.frame.size.height-200) ; }
你将会添加下面的三个方法到ViewController中去处理album的三个管理操作:添加,删除和撤销。
第一个方法是增加一个新的album:
- (void)addAlbum:(Album *)album atIndex:(int)index { [[LibraryAPIsharedInstance] addAlbum:album atIndex:index]; currentAlbumIndex = index; [selfreloadScroller]; }
在这里你添加了一个album,设置它的当前的album的index,然后重新加载scroller.接下来删除方法:
- (void)deleteAction { Album *deleteAlbum = [allAlbumsobjectAtIndex:currentAlbumIndex]; NSMethodSignature *sig = [selfmethodSignatureForSelector:@selector(addAlbum:atIndex:)]; NSInvocation*undoAction = [NSInvocationinvocationWithMethodSignature:sig]; undoAction.target = self; [undoAction setSelector:@selector(addAlbum:atIndex:)]; [undoAction setArgument:&deleteAlbumatIndex:2]; [undoAction setArgument:¤tAlbumIndexatIndex:3]; [undoAction retainArguments]; [undoStackaddObject:undoAction]; [[LibraryAPIsharedInstance] deleteAlbumAtIndex:currentAlbumIndex]; [selfreloadScroller]; [toolBar.items[0] setEnabled:YES]; }
在上面的这些代码里面有一些新的有趣的特性,所以解析一下:
1. 获得要删除的album,
2. 定义一个NSMethodSignature类型去创建一个NSInvocation,它将用于去在用户决定撤销一个删除时做和删除相
反的操作。这个NSInovation需要知道三个事情:The
selector(发送的消息),目标:((发送给谁)和发送的消息的一些参数。在这个例子里,消息时发送给删除的相反方,因为当你撤销一个删除操作,你
需要将它们重新添加这个删除的album。、
3. 当这个undoAction被创建后你将它添加到undoStack中,这个动作将会被添加到一个数组的末尾,正像一个普通的堆栈一样。
4. 使用LibraryAPI去从数据结构中去删除album然后重新加载scroller。
5. 因为在undoStack中有了一个动作,所以你需要去将undo按钮使能。
注意:当你使用NSInvocation,你需要去记住下面的三个点:
1. 参数必须通过指针来传递。
2.参数起始于index 2,因为0和1是保留给target 和action用的。
3. 如果这些参数有可能被销毁(dealocate),你应该使用retainArguments.
最后添加下面的代码用于撤销:
- (void)undoAction { if ([undoStackcount] > 0) { NSInvocation*undoAction = [undoStacklastObject]; [undoStackremoveLastObject]; [undoActioninvoke]; }elseif( [undoStackcount] == 0){ [toolBar.items[0] setEnabled:NO]; } }
这个撤销操作会pops堆栈中的最后一个对象。这个对象永远是NSInvocation类型的而且可以通过调用invoke被激活。这回激活你你先
前在album被删除时创建的命令,然后添加背删除的album到album列表中。因为你也可以删除在你撤销后最后一个对象,所以你需要判断这个栈是否
时空的,如果是的话那么将undo按钮禁止交互。编译和运行你的app去特使这个undo 机制。删除这个album然后点击撤销操作去看看效果。
还有两种设置模式没用将它们应用在这个app中,但是也是很重要的:抽象工厂模式和责任链模式。但官方文档去扩展你的设计模式水平
在这个教程你已经看到怎么样去将发挥ios的设计模式的威力用很简单和耦合性地的方式去处理很复杂的任务。你已经学习了很多关于ios设计模式的概念:MVC, 但里,代理,协议,外观,贯彻着,备忘录,命令。
你最后的代码是耦合性很低的,可重用的且可读性该,如果其他开饭看你的代码它会立刻明白这是怎么回事和每个类的作用。
关键不是使用设计模式去写你的每一行代码而是当你在解决一个困难的问题特别是在设计应用的早期的时候意识到使用什么设计模式。它会让你的开发工作更容易,代码更高效。