模型-視圖-控制器(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。其實是觀察者模式。
這裏,發送通知的對象,並不知道誰會收到這個通知。這樣通知者和觀察者之間鬆散耦合。