模型-视图-控制器(MVC)模式-设计模式之王html
模型-视图-控制器(MVC)是Cocoa的构建块之一,毫无疑问它是使用最频繁的设计模式。它根据通用的角色去划分类,这样就使得类的java
职责能够根据角色清晰的划分开来。ios
涉及到的三个角色以下:
Model:
模型保存应用程序的数据,定义了怎么去操做它。例如在本应用中模型就是Album类。
View:
视图是模型的可视化表示以及用户交互的控件;基本上来讲,全部的UIView对象以及它的子类都属于视图。在本应用中AlbumView表明了视图。
Controller:
控制器是一个协调全部工做的中介者(Mediator)。它访问模型中的数据并在视图中展现它们,同时它们还监听事件和根据须要操做数据。你能够猜猜哪一个类是控制器吗?它正是:ViewController。objective-c
一个MVC模式的好的实现也就意味着每个对象都会被划分到上面所说的组中。设计模式
咱们能够很好的用下图来描述经过控制器实现的视图到模型的交互过程:数组
模型会把任何数据的变动通知控制器,而后控制器更新视图数据。视图对象通知控制器用户的操做,控制器要么根据须要来更新模型,要么检索任何被请求的数据。安全
你可能在想为何不能仅仅使用控制器,在一个类中实现视图和模型,这样貌似更加容易架构
全部的这些都归结于代码关注点分离以及复用。在理想的状态下,视图应该和模型彻底的分离。若是视图不依赖某个实际的模型,那么视图就能够被复用来展现不一样模型的数据。app
举个例子来讲,若是未来你打算加入电影或者书籍到你的资料库中,你仍然可使用一样的AlbumView去显示电影和书籍数据。更进一步来讲,若是你想建立一个新的与专辑有关联的工程,你能够很简单的复用Album类,由于它不依赖任何视图。这就是MVC的强大之处。框架
如何使用MVC模式
首先,你须要确保在你工程中的每一个类是控制器,模型和视图中的一种,不要在一个类中组合两种角色的功能。到目前为止,你建立了一个Album类和AlbumView类,这样作挺好的。
其次,为了确保你能符合这种工做方法,你应该建立三个工程组(Project Group)来保存你的代码,每一个工程组只存放一种类型的代码。
导航到”文件\新建\组(File\New\Group)”(或者按下Command + Option + N),命名组为Model,重复一样的过程来建立View和Controller组。
如今拖动Album.h和Album.m去模型组,拖动AlbumView.h和AlbumView.m去视图组,最后拖动ViewController.h和ViewController.m到控制器组。
此时工程结构应该看起来和下图相似:
没有了以前全部文件都散落在各处,如今你的工程已经开起来好多了。显然你也能够有其它的组和类,可是本应用的核心包含在这三个类别中(Model,View,Controller)。
如今全部的组件都已经安排好了,你须要从某处获取专辑数据。你将建立一个贯穿于代码的管理数据的API-这也就表明将有机会去讨论下一个设计模式-单例(单态)模式。
单例(单态)模式
单例设计模式确保对于一个给定的类只有一个实例存在,这个实例有一个全局惟一的访问点。它一般采用懒加载的方式在第一次用到实例的时候再去建立它。
注意:苹果大量使用了此模式。例如:[NSUserDefaults standardUserDefaults],[UIApplication sharedApplication],[UIScreen mainScreen],[NSFileManager defaultManager],全部的这些方法都返回一个单例对象。
你极可能会想为何这么关心是否一个类有多个实例?毕竟代码和内存都是廉价的,对吗?
有一些状况下,只有一个实例显得很是合理。举例来讲,你不须要有多个Logger的实例,除非你想去写多个日志文件。或者一个全局的配置处理类:实现线程安全的方式访问共享实例是容易的,好比一个配置文件,有好多个类同时修改这个文件。
如何使用单例模式
首先来看看下面的图:
上面的图描述了一个有单一属性(它就是单一实例)和sharedInstance,init两个方法的类。客户端第一次发送sharedInstance消息的时候,instance属性还没有被初始化,因此此时你须要建立一个新的实例,而后返回它的引用。
当你下一次调用sharedInstance的时候,instance不须要任何初始化能够当即返回。这个逻辑保证老是只有一个实例。你接下来将用这个模式来建立一个管理全部专辑数据的类。你将注意到工程中有一个API的组,在这个组里你能够放入给你应用提供服务的全部类。在此组中,用IOS\Cocoa Touch\Objective-C class模板建立一个新类,命名它为LibraryAPI,设置父类为NSObject.
打开LibraryAPI.h,用以下代码替换它的内容:
1
2
3
4
5
|
@interfaceLibraryAPI : NSObject
+ (LibraryAPI*)sharedInstance;
@end
|
如今打开LibraryAPI.m,在@implementation那一行后面插入下面的方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
+ (LibraryAPI*)sharedInstance
{
// 1
static LibraryAPI *_sharedInstance = nil;
// 2
static dispatch_once_t oncePredicate;
// 3
dispatch_once(&oncePredicate, ^{
_sharedInstance = [[LibraryAPI alloc] init];
});
return _sharedInstance;
}
|
在这个简短的方法中,有一些须要须要注意的点:
下一次你调用sharedInstance的时候,dispatch_once块中的代码将不会执行(由于它已经被执行了一次),你将获得原先已经初始化好的实例。
注意:为了学习更多关于GCD方面的信息以及如何使用,请查看本站指南Multithreading and Grand Central Dispatch和How to Use Blocks。
你如今有一个单例的对象做为管理专辑数据的入口。咋们更进一步来建立一个处理资料库数据持久化的类。
在API组中,使用iOS\Cocoa Touch\Objective-C class模板建立一个新类,命名它为PersistencyManager,设置父类为NSObject.
打开PersistencyManager.h在文件头部增长下面的导入语句:
#import “Album.h”
接下来,在PersistenceManager.h文件的@interface以后,增长下面的代码:
1
2
3
|
- (NSArray*)getAlbums;
- (void)addAlbum:(Album*)album atIndex:(int)index;
- (void)deleteAlbumAtIndex:(int)index;
|
上面是你须要处理专辑数据的方法的原型。
打开PersistencyManager.m文件,在@implementation行以前,增长下面的代码:
1
2
3
4
|
@interfacePersistencyManager () {
// an array of all albums
NSMutableArray *albums;
}
|
上面增长了一个类扩张(class extension),这是另一个增长私有方法和变量以致于外部类不会看到它们的方式。这里,你申明了一个数组NSMutableArry来保存专辑数据。这个数组是可变的方便你增长和删除专辑。
如今在PersistencyManager.m文件中@implementation行以后增长以下代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
- (id)init
{
self = [super init];
if (self) {
// a dummy list of albums
albums = [NSMutableArrayarrayWithArray:
@[[[Album alloc] initWithTitle:@"Best of Bowie" artist:@"David Bowie" coverUrl:@"http://www.coversproject.com/static/thumbs/album/album_david%20bowie_best%20of%20bowie.png" year:@"1992"],
[[Album alloc] initWithTitle:@"It's My Life" artist:@"No Doubt" coverUrl:@"http://www.coversproject.com/static/thumbs/album/album_no%20doubt_its%20my%20life%20%20bathwater.png" year:@"2003"],
[[Album alloc] initWithTitle:@"Nothing Like The Sun" artist:@"Sting" coverUrl:@"http://www.coversproject.com/static/thumbs/album/album_sting_nothing%20like%20the%20sun.png" year:@"1999"],
[[Album alloc] initWithTitle:@"Staring at the Sun" artist:@"U2" coverUrl:@"http://www.coversproject.com/static/thumbs/album/album_u2_staring%20at%20the%20sun.png" year:@"2000"],
[[Album alloc] initWithTitle:@"American Pie" artist:@"Madonna" coverUrl:@"http://www.coversproject.com/static/thumbs/album/album_madonna_american%20pie.png" year:@"2000"]]];
}
return self;
}
|
在init中,你用五条样例专辑填充数组。若是你不喜欢上面的专辑,你能够自由用你喜欢的专辑替换它们。
如今在PersistencyManager.m文件中增长下面的三个方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
- (NSArray*)getAlbums
{
return albums;
}
- (void)addAlbum:(Album*)album atIndex:(int)index
{
if (albums.count >= index)
[albums insertObject:album atIndex:index];
else
[albums addObject:album];
}
- (void)deleteAlbumAtIndex:(int)index
{
[albums removeObjectAtIndex:index];
}
|
这些方法让你能够增长和删除专辑。构建你的工程确保每一个资源均可以被正确的编译。
这时候,你可能想知道PersistencyManager类来自哪里?由于它不是一个单例类。下一部分,咱们将探究LibraryAPI和PersistencyManager之间的关系,那时候你将看到门面或者外观(Facade)模式。
概念:整个应用或系统只能有该类的一个实例
在iOS开发咱们常常碰到只须要某类一个实例的状况,最多见的莫过于对硬件参数的访问类,好比UIAccelerometer.这个类能够帮助咱们得到硬件在各个方向轴上的加速度,可是咱们仅仅须要它的一个实例就够了,再多,只会浪费内存。
因此苹果提供了一个UIAccelerometer的实例化方法+sharedAccelerometer,从名字上咱们也能看出此方法让整个应用共享一个UIAccelerometer实例(PS:iOS 的开放中,咱们每每能从方法名中就了解这个方法的做用),它内部的如何实现咱们暂且不谈,先来看看还有哪些类一样使用了单例模式。
等等,苹果的SDK中大量的遵循此设计模式,那么它的内部是如何实现的呢?
首先给你们介绍一下GCD技术,是苹果针对于多核CPU的多任务解决方案。你不须要了解更多,只须要知道是一组基于C语言开发的API(详细内容能够看一下唐巧前辈的这篇博文:http://blog.devtang.com/blog/2012/02/22/use-gcd/ )。GCD提供了一个dispatch_once函数,这个函数的做用就是保证block(代码块:暂时理解为一个跟函数相近的东西,具体能够参照这篇文章:http://blog.csdn.net/enuola/article/details/8674063 )里的语句在整个应用的生命周期里只执行一次。
OK,接下来就给出一个使用了单例模式新建和获取实例的类模版,代码以下:
//Singleton.h @interface Singleton : NSObject + (Singleton *)sharedSingleton; <1> @end /***************************************************************/ //Singleton.m #import "Singleton.h" @implementation Singleton static Singleton *sharedSingleton = nil;<2> + (Singleton *)sharedSingleton{ static dispatch_once_t once;<3> dispatch_once(&once,^{ sharedSingleton = [[self alloc] init];<4> //dosometing }); return sharedSingleton;<5> }
上述代码中有5小步,解释以下:
OK,这就是iOS开发中单例模式的机制,下面咱们就看看如何在实际开发中使用此模式?(PS:为了尽量的突出核心内容,咱们会对设计中的其余模式或内容一笔带过)
假如咱们须要在iOS应用中实现分层的架构设计,即咱们须要把数据的持久层,展现层,和逻辑层分开。为了突出重点,咱们直接把目光转到持久层,而根据MVC的设计模式,咱们又能够把持久层细分为DAO层(放置访问数据对象的四类方法)和Domain层(各类实体类,好比学生),这样就可使用DAO层中的方法,配合实体类Domain层对数据进行清晰的增删改查。那么咱们如何设计呢?
从使用者的角度看,咱们指望得到DAO层的类实例,而后调用它的增删改查四大方法。但是这个类实例,咱们彷佛只须要一个就足够了,再多的话不利于管理且浪费内存。OK,咱们可使用单例模式了,代码以下:
.h文件:
//StudentDAO.h @interface StudentDAO:NSObject @property (nonatomic,strong) NSMutaleArray *StudentsInfo; + (StudentDAO *)sharedStudentDAO; -(int) create:(Student*)student; -(int) remove:(Student*)student; -(int) modify:(Student*)student; -(NSMutaleArray) findAll; @end
.m文件:
//StudentDAO.m #import "StudentDAO.h" #import "Student.h" @implementation StudentDAO static StudentDAO *studentDao = nil; + (StudentDAO)sharedStudentDAO{ static dispatch_once_t once; dispatch_once(&once,^{ Student *student1 = [[Student alloc]init]; student1.name = "MexiQQ"; student1.studentNum = "201200301101"; Student *student2 = [[Student alloc]init]; student2.name = "Ricardo_LI"; student2.studentNum = "201200301102"; studentDao = [[self alloc] init]; studentDao._StudentsInfo = [[NSMutaleArray alloc]init]; [studentDao._StudentsInfo addObject:student1]; [studentDao._StudentsInfo addObject:student2]; }); return studentDao; } //插入的方法 -(int)create:(Student*)stu{ [self._StudentsInfo addObject:stu]; return 0; } //删除的方法 -(int)remove:(Student*)stu{ for(Student* s in self._StudentsInfo){ if([stu.studentNum isEqual:s.studentNum]){ [self._StudentsInfo removeObject:s] break; } } } -(int)modify...... //省略不写 -(NSMutaleArray)findAll...... //省略不写
上述例子不难理解,其中用到的Student类我这里就不给出了,只是一个含有姓名和学号属性的实体类。
概念:一个对象状态改变,通知正在对他进行观察的对象,这些对象根据各自要求作出相应的改变
图例:
如图所示:操做对象向被观察者对象投送消息,使得被观察者的状态得以改变,在此以前已经有观察者向被观察对象注册,订阅它的广播,如今被观察对象将本身状态发生改变的消息广播出来,观察者接收到消息各自作出应变。
OK,咱们先来看看在苹果的Cocoa Touch框架中有谁使用了观察者模式:
原理图以下:
如图所示,在通知机制中对某个通知感兴趣的全部对象均可以成为接受者。首先,这些对象须要向通知中心(NSNotificationCenter)发出addObserver:selector:name:object:消息进行注册,在投送对象投送通知送给通知中心时,通知中心就会把通知广播给注册过的接受者。全部的接受者不知道通知是谁投送的,不去关心它的细节。投送对象和接受者是一对多的关系。接受者若是对通知再也不关注,会给通知中心发送removeObserver:name:Object:消息解除注册,之后再也不接受通知。
(ps:这段话内容摘抄自关东升先生的文章)
OK,咱们试着去使用一下通知机制:
新建一个Single view Project,对项目中的文件作如下修改:
AppDelegate.m
- (void)applicationDidEnterBackground:(UIApplication *)application { [[NSNotificationCenter defaultCenter]postNotificationName:@"APPTerminate" object:self]; }
ViewController.m
// // ViewController.m // TestNotification // // Created by liwenqian on 14-10-18. // Copyright (c) 2014年 liwenqian. All rights reserved. // #import "ViewController.h" @interface ViewController () @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; //注意此处的selector有参数,要加冒号 [[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(doSomething:) name:@"APPTerminate" object:nil]; // Do any additional setup after loading the view, typically from a nib. } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; [[NSNotificationCenter defaultCenter]removeObserver:self]; // Dispose of any resources that can be recreated. } #pragma mark -处理通知 -(void)doSomething:(NSNotification*)notification{ NSLog(@"收到通知"); } @end
如上所示,对模版项目的两个文件的方法或整个文件作出修改,Command+R运行你的APP,再按下Home键(Command+H),会发现打印出一行收到通知的文字,以下:
在APP退到后台时,发出广播,而viewController由于时观察者,收到广播,执行doSomething方法,打印出收到广播的文字。
原理图以下:
如图所示:
该机制下观察者的注册是在被观察者的内部进行的,不一样于通知机制(由观察者本身注册),须要被观察者和观察者同时实现一个协议:NSKeyValueObserving,被观察者经过addObserver:forKeypath:options:context方法注册观察者,以及要被观察的属性。
新建一个single view project,同时新建一个继承自NSObject的TestWatche类:文件结构以下图:
对文件进行以下修改:
AppDelegate.h
// // AppDelegate.h // TestNotification // // Created by liwenqian on 14-10-18. // Copyright (c) 2014年 liwenqian. All rights reserved. // #import <UIKit/UIKit.h> #import <CoreData/CoreData.h> #import "TestWatche.h" @interface AppDelegate : UIResponder <UIApplicationDelegate> @property (strong, nonatomic) UIWindow *window; @property (readonly, strong, nonatomic) NSManagedObjectContext *managedObjectContext; @property (readonly, strong, nonatomic) NSManagedObjectModel *managedObjectModel; @property (readonly, strong, nonatomic) NSPersistentStoreCoordinator *persistentStoreCoordinator; @property (strong,nonatomic) NSString *state; @property (strong,nonatomic) TestWatche *watcher; - (void)saveContext; - (NSURL *)applicationDocumentsDirectory; @end
AppDelegate.m 对以下方法作出修改
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { // Override point for customization after application launch. self.watcher = [TestWatche alloc]; [self addObserver:self.watcher forKeyPath:@"state" options:NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew context:@"pass content"]; self.state = @"launch"; return YES; } - (void)applicationDidEnterBackground:(UIApplication *)application { self.state = @"backgroud"; }
TestWatche.m(因为继承自NSObject,而NSObject已实现了NSKeyValueObserving协议,因此不须要作声明)
// // TestWatche.m // TestNotification // // Created by liwenqian on 14-10-18. // Copyright (c) 2014年 liwenqian. All rights reserved. // #import "TestWatche.h" @implementation TestWatche -(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{ NSLog(@"state change:%@",change); } @end
OK,Command+B Command+R Command+H看看你的应用输出了什么,若是你的操做无误的话,会显示以下内容:
我的认为委托模式大多数人解释的复杂了,其实就像是java中的接口,类能够实现或不实现协议(接口)中的方法。经过此种方式,达到最大的解耦目的,方便项目的扩展。不过你须要设置应用的委托对象,以肯定协议中的方法为谁服务。
拿最经常使用的UITableViewDelegate UITableViewDataSource来举例:
实现一个页面有一个UItableView,UItableView的每一栏(cell)的数据由咱们指定,那么咱们该如何作呢?苹果也天然想到了这一点,因而定义了一个接口,这个接口有许多的方法,只须要咱们把要服务的对象传进去,就可使用这些方法了,这个接口就是委托和协议。而UITableViewDelegate 和 UITableViewDataSource 就是专为UITableView而写的委托和协议。用法以下:
ViewController.h
// // ViewController.h // RecipeBookMe // // Created by liwenqian on 14-9-10. // Copyright (c) 2014年 liwenqian. All rights reserved. // #import <UIKit/UIKit.h> @interface ViewController : UIViewController <UITableViewDelegate, UITableViewDataSource> @property (nonatomic, strong) IBOutlet UITableView *mytableView; @end
ViewController.m
// // ViewController.m // RecipeBookMe // // Created by liwenqian on 14-9-10. // Copyright (c) 2014年 liwenqian. All rights reserved. // #import "ViewController.h" #import "DetailViewController.h" #import "Recipe.h" #import "RecipeTableCellTableViewCell.h" @interface ViewController () @end @implementation ViewController{ NSArray *recipes; } @synthesize mytableView; - (void)viewDidLoad { [super viewDidLoad]; // Initialize the recipes array Recipe *recipe1 = [Recipe new]; recipe1.name = @"Egg Benedict"; recipe1.prepTime = @"30 min"; recipe1.image = @"egg_benedict.jpg"; recipe1.ingredients = [NSArray arrayWithObjects:@"2 fresh English muffins", @"4 eggs", @"4 rashers of back bacon", @"2 egg yolks", @"1 tbsp of lemon juice", @"125 g of butter", @"salt and pepper", nil]; Recipe *recipe2 = [Recipe new]; recipe2.name = @"Mushroom Risotto"; recipe2.prepTime = @"30 min"; recipe2.image = @"mushroom_risotto.jpg"; recipe2.ingredients = [NSArray arrayWithObjects:@"1 tbsp dried porcini mushrooms", @"2 tbsp olive oil", @"1 onion, chopped", @"2 garlic cloves", @"350g/12oz arborio rice", @"1.2 litres/2 pints hot vegetable stock", @"salt and pepper", @"25g/1oz butter", nil]; recipes = [NSArray arrayWithObjects:recipe1, recipe2, nil]; } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; } /*--------------------------------------------------------------------*/ //定义UITableview的栏目数量 - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return [recipes count]; } //定义UITableviewCell的显示内容 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *CoustomerTableIdentifier = @"RecipeTableCellTableViewCell"; RecipeTableCellTableViewCell *cell =(RecipeTableCellTableViewCell *) [tableView dequeueReusableCellWithIdentifier:CoustomerTableIdentifier]; if (cell == nil) { cell = [[RecipeTableCellTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CoustomerTableIdentifier]; } recipe = [recipes objectAtIndex:indexPath.row]; cell.nameLabel.text = recipe.name; cell.prepTimeLabel.text= recipe.prepTime; cell.thumbnailImageView.image = [UIImage imageNamed:recipe.image]; return cell; } //点击每一栏执行跳转时的处理 - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { if ([segue.identifier isEqualToString:@"showRecipeDetail"]) { NSIndexPath *indexPath = nil; Recipe *recipe = nil; indexPath = [self.mytableView indexPathForSelectedRow]; recipe = [recipes objectAtIndex:indexPath.row]; DetailViewController *destViewController = segue.destinationViewController; destViewController.recipe = [recipes objectAtIndex:indexPath.row]; } } //定义cell的高度 - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { return 71; } /*--------------------------------------------------------------------*/ @end
如上所示,两条/------/注释间的方法所有来自于委托和协议。利用委托和协议,你能够把主要精力放到逻辑业务上,将数据绑定和事件处理交给委托和协议去完成。
工厂模式个人理解是:他就是为了建立对象的
建立对象的时候,咱们通常是alloc一个对象,若是须要建立100个这样的对象,若是是在一个for循环中还好说,直接一句alloc就好了,可是事实并不那么如意,咱们可能会在不一样的地方去建立这个对象,那么咱们可能须要写100句alloc 了,可是若是咱们在建立对象的时候,须要在这些对象建立完以后,为它的一个属性添加一个固定的值,比方说都是某某学校的学生,那么可能有须要多些100行重复的代码了,那么,若是写一个-(void)createObj方法,把建立对象和学校属性写在这个方法里边,那么就是会省事不少,也就是说咱们能够alloc 建立对象封装到一个方法里边,直接调用这个方法就能够了,这就是简单工厂方法
可是工厂方法也有它的限制:
1.要建立的类必须拥有同一个父类
2.要建立的类在100个不一样的地方所调用的方法必须同样
工厂方法模式:定义建立对象的接口,让子类决定实例化哪个类。工厂方法使得一个类的实例化延迟到其子类。
在Objective-C中两步对象建立法[[SomeClass alloc] init].可是还有一些便利的建立方法。例如,NSNumber有不少numberWith*方法;其中有两个是numberWithBool:和numberWithChar:。它们是类方法,也就是说咱们向NSNumber发送[[NSNumber numberWithBool:bool]]与[[NSNumber numberWithChar:char]],以得到与传入参数同类型的各类NSNumber实例。与如何建立NSNumber的具体子类型的实例有关的细节,都有NSNumber的类工厂方法负责。[[NSNumber numberWithBool:bool]]的状况是,方法接受值bool,并把NSNumber的内部子类的一个实例初始化,让它可以反应传入的值。
一、定义经过工厂方法建立的类的统一父类(例如:Animal)和经过工厂方法建立的各个类(例如:Dog、Cat等),并为各个子类提供初始化细节。
二、定义生成器父类(例如:AnimalGenerator)和各个生成器子类(例如:DogGenerator、CarGenerator)。//经过工厂方法建立类的实现举例
- (id)initWithName:(NSString*)name{
if(self = [super init]){ self.name = name; //添加初始化相关细节 ... } return self; }
三、生成器父类中建立工厂方法、各个生成器子类中重载该方法
//AnimalGenerator中工厂方法 - (Animla*)animalWithName:(NSString*)name{ return [[Animal alloc] initWithName:name]; } //DogGenerator对AnimalGenerator中工厂方法的重载 - (Animal*)animalWithName:(NSString*)name{ return [[Dog alloc] initWitName:name]; }
四、使用
DogGenerator *dogGenerator = [[DogGenerator alloc] init];
//不一样生成器建立不一样的动物 Animal *dog = [[dogGenerator animalWithName:@"小七"]];
下面分别介绍一下各个设计模式的用途。
这是Apple的权威解释。
delegation,委托模式(另外有个经常使用的proxy模式,两者的区别是代理模式通常要更严格,实现相同的接口,委托只是引用被委托对象),是简单的强大的模式,可以让一个对象扮演另外对象的行为。委托对象保持到另外对象的引用,并在适当的时候发消息给另外对象。委托对象能够在发送消息的时候作一些额外的事情。
在cocoa框架中的委托模式,委托对象每每是框架中的对象,被委托对象是自定义的controller对象。委托对象保持一个到被委托对象的弱引用。
在该文档中所举的例子是mac下开发的示例。
这里被委托对象是NSWindow类的一个实例,该类声明了协议(protocol),其中有个方法是windowShouldClose,当用户点击窗口的关闭按钮的时候,窗口对象将发送windowShouldClose消息给代理对象(windowDelegate),询问是否确认关闭窗口。代理对象返回一个bool值,所以控制了窗口对象的行为。
在iOS中也有相似的示例,在建立的项目中,classes目录中会有xxAppDelegate,这就是委托类。在该委托类实例中,引用了UIWindow和Controller。能够相似上面mac示例控制被引用对象的行为。
能够认为是java中的接口(interface)。在iOS中有两种protocol:
MVC设计模式你们应该很熟悉了,很少说了。
target-action是个设计模式。对象保持必要的信息,当事件发生的时候发送消息给其余对象。所保持的信息有两部分数据组成:
当被称做action message的事件发生,将向target发送action selector定义的方法消息。
target-action模式通常用于自定义的controller按照应用规范定义的方式处理action message。
一个notification,即一个通知,是一个消息,是用于通知一到多个观察者对象程序当前有一个事件发生。这里,接收通知的一方叫观察者,observer。其实是观察者模式。
这里,发送通知的对象,并不知道谁会收到这个通知。这样通知者和观察者之间松散耦合。