您好,登錄后才能下訂單哦!
裝飾設計模式動態的添加一些行為和任務到一個對象中且不需要去修改它的代碼。當然你也可以選擇用繼承的方式-通過包裝成另一個對象去改變它的行為。
在objective-c中由兩個非常常用的實現方式:分類和代理。(Category, Delegate)
分類是一種非常有用的機制,它允許你去添加一些方法到已經存在的類中且不用去繼承它。這些新方法會在編譯的時候添加上去,且可以像這個被擴展的類中的其他方法一樣被執行。它和典型的裝飾設計模式由一點輕微的不同,因為它并不hold住它所擴展的類的實例。
小貼士:除了向你自己的類去添加方法,你還可以向cocoa中的類去添加方法。
想像這是你想要在tableView中顯示album數據的一種方案。
這些album titles從哪里來?Album是一個模型對象,所以它并不在意你是怎么樣顯示數據的,你將要需要一些額外的代碼去將這些功能添加到Album類中去,但是不要直接去修改這些類。
你將要創建一個分類去擴展Album。它要定義一個新的方法去返回一個數據結構來被UITableViews輕松的使用。這些數據結構看起來應該是這樣的。
為了添加一個分類到Album中,新建一個文件選擇Objective-C category 模版,在category field中鍵入TableRepresentation,在Category On上寫入Album。
在Album+TableRepresentation.h加入以下方法的聲明。
- (NSDictionary *)tr_tableRepresentation;
注意到這里有一個tr_在方法名的前面,就像一個分類的名字的前綴。這樣的命名約定有利于防止和其他方法沖突。
注意:如果在分類中聲明的方法名字和在原來的類中的方法名字一樣,或者和另一個類擴展中的方法名字一樣,那么就會顯示未定義,因為這些方法的實現是在運行時,如果你用分類去擴展你自己定義的類,那么出現問題的概率不大,但是如果你用分類添加方法到Cocoa或者CocoaTouch的類中,那有可能會產生很嚴重的問題。
在Album+TableRepresentation.m添加下面的方法
- (NSDictionary *)tr_tableRepresentation { return @{@"title": @[@"Artist",@"Album",@"Genre",@"Year"], @"values": @[self.artist, self.title, self.genre, self.year]}; }
想一下這個設計模式有多強大:
1你可以直接使用Album中的屬性。
2 你可以不繼承就可以向一個類添加方法。當然如果你想繼承的話,也可以繼承。
3 它可以簡單的讓你返回一個UITableView-ish的Album的顯示類型,而不用修改Album的代碼。
蘋果公司使用分類在很多在基礎的類上。要想去看他們是怎樣實現的,可以打開NSString.h,找到@interface NSString ,那你將會看到這個類的定義和一下三個分類緊緊聯系在一起:
NSStirngExtensionMethods, NSExtendedStringPropertyListParsing,NSStringDeprecated。分類把這些方法有序的組織起來而又分成幾部分。
另一個裝飾設計模式就是代理,就是一個對象可以代表或者協助另一個對象的一種機制。例如,當你使用UITableView,其中你必須實現的方法就是tableView:number numberOfRowsInSection.
你不能指望UITableView去知道你想在每一個分區里面有多少行。因此,計算每個分區有多少行的任務就交給了UITableView的代理。這讓UITableView可以和它要顯示的數據進行獨立
UITableView 對象的工作是顯示一個table view,然后最終它還是需要一些它根本沒有擁有的數據。那么,它會向它的代理去發送一條消息去請求一些額外的信息。在Objective-C中的代理設計模式的實現中,一個類可以通過協議protocol聲明必須的required或者可選的optional的方法。你將會實現這些協議在這個教程的后半部分。
似乎看起來通過繼承一個對象然后去重寫它的需要的方法更容易,凡事考慮到你你只能繼承一個類。如果你向一個對象成為兩個或者更多對象的代理,那你不能通過繼承去實現這個目標。
小貼士:這是一個很重要的模式,蘋果公司使用這個方法在大部分的UIKit的類中:UITableView,UITextView,UITextField,UIWebView,UIAlert,UIAction,UICollectionView,UIPickerView,UIGestureRecognizer,UIScrollerView等等。
在ViewController.m中加入導入這些文件的頭文件。
#import "LibraryAPI.h" #import "Album+TableRepresentation.h"
利用類擴展去添加這些私有變量。
@interfaceViewController () { UITableView *dataTable; NSArray *allAlbums; NSDictionary*albumData; intcurrentAlbumIndex; } @end
然后在@interfaceViewController ()加上 <UITableViewDataSource,UITableViewDelegate>
這是你怎樣使你的代理遵從一個協議-想像它是一個被代理去完成方法的協議的一個約定,在這里你讓ViewController去遵守UITableViewDataSource和UITableViewDelegate協議,這個方法使得UITableView可以絕對保證這些必須方法會被它的代理所實現。
接下來,在viewDidLoad中加入這些代碼:
self.view.backgroundColor = [UIColorcolorWithRed:0.76f green:0.81f blue:0.87f alpha:1]; currentAlbumIndex = 0; allAlbums = [[LibraryAPIsharedInstance]getAlbums]; dataTable = [[UITableViewalloc]initWithFrame:CGRectMake(0, 120, self.view.frame.size.width, self.view.frame.size.height-120) style:UITableViewStyleGrouped]; dataTable.delegate = self; dataTable.dataSource = self; dataTable.backgroundColor = nil; [self.viewaddSubview:dataTable];
這里是這些代碼的講解:
1首先將背景顏色改成了一個相對友好的背景色。
2通過API而不是PersistencyManager得到了albums的列表。
3在這里創建了UITableView,你聲明了這個控制器是UITableView的delegate/dataSource,因此UITableView的所有必須的方法都會由控制器提供。
然后添加這個方法在控制器實現代碼中:
- (void)showDataForAlbumAtIndex:(int)index { if (index < [allAlbumscount]) { Album *album = [allAlbumsobjectAtIndex:index]; albumData = [album tr_tableRepresentation]; }else{ albumData = nil; } [dataTablereloadData]; }
showDataForAlbumAtIndex:從albums這個數組中獲取了所需答album的數據。然后你之需要去調用reloadData.這會讓UITableView詢問它的代理諸如在table view的每個分區中顯示多少行,多少個分區,每一行應該怎么樣之類的事情。
在viewDidLoad的結尾處加上[self showDataForAlbumAtIndex:currentAlbumIndex];
這會在應用啟動的時候load現在的album,然后因為currentAlbumIndex原先被初始化為0,所以只是在會顯示第一個album。
編譯運行你的工程,你會遇到一個崩潰伴隨一個異常的顯示在調試控制臺。
這怎么了?因為你聲明了控制器成為UITableView的delegate和dataSource,但是這樣的話你必須遵守去實現它的必須的方法包括numberOfRowsInSection這個你還沒實現的方法。向ViewController.m中加入這兩個方法。
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return [albumData[@"title"] count]; } -(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { UITableViewCell *cell =[tableView dequeueReusableCellWithIdentifier:@"Album"]; if (!cell) { cell = [[UITableViewCellalloc]initWithStyle:UITableViewCellStyleValue1reuseIdentifier:@"Album"]; } cell.textLabel.text = [albumData [@"title"] objectAtIndex:indexPath.row]; cell.detailTextLabel.text = [albumData[@"values"]objectAtIndex:indexPath.row]; return cell; }
前一個方法返回在table view中要顯示的行數,在這里和數據結構中的titles的數量相同。后者創建并返回了一個帶有title和value的cell。
編譯和運行工程,你的應用應該是向這樣展示在你的面前。
到目前為止,事情好像看起老很棒,但是你如果你調用第一張圖片去展示啟動后的app,那將會由一個水平的滾動條在屏幕的albums轉換之間的頂部。與其創建一個單一用途的水平滾動條,為什么不使它成為一個通用的視圖。
為了是這個視圖可重用,所有的關于它的內容的決定都該留給另一個對象-它的代理。這個horizontal scroller應該定義一些方法讓它的代理去實現以至于去和scroller一起工作,和UITableViewde的代理方法想類似。我們將會在下一個設計模式中去討論這個設計模式。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。