深入解析設(shè)計(jì)模式中的裝飾器模式在iOS應(yīng)用開(kāi)發(fā)中的實(shí)現(xiàn)
裝飾器模式可以在不修改代碼的情況下靈活的為一對(duì)象添加行為和職責(zé)。當(dāng)你要修改一個(gè)被其它類包含的類的行為時(shí),它可以代替子類化方法。
一、基本實(shí)現(xiàn)
下面我把類的結(jié)構(gòu)圖向大家展示如下:
讓我們簡(jiǎn)單分析一下上面的結(jié)構(gòu)圖,Component是定義一個(gè)對(duì)象接口,可以給這些對(duì)象動(dòng)態(tài)地添加職責(zé)。ConcreteComponent是定義了一個(gè)具體的對(duì)象,也可以給這個(gè)對(duì)象添加一些職責(zé)。Decorator,裝飾抽象類,繼承了Component,從外類來(lái)擴(kuò)展Component類的功能,但對(duì)于Component來(lái)說(shuō),是無(wú)需知道Decorator的存在的。至于ConcreteDecorator就是具體的裝飾對(duì)象,起到給Component添加職責(zé)的功能。
下面,還是老套路,我會(huì)盡可能的給出Objective C實(shí)現(xiàn)的最簡(jiǎn)單的實(shí)例代碼,首先聲明一下,這些代碼是運(yùn)行在ARC環(huán)境下的,所以對(duì)于某些可能引起內(nèi)存泄漏的資源并沒(méi)有采用手動(dòng)釋放的方式,這一點(diǎn)還是需要大家注意。
注意:本文所有代碼均在ARC環(huán)境下編譯通過(guò)。
Components類接口文件
#import<Foundation/Foundation.h>
@interface Components :NSObject
-(void) Operation;
@end
Components類實(shí)現(xiàn)文件
#import"Components.h"
@implementation Components
-(void)Operation{
return;
}
@end
Decorator類接口文件
#import"Components.h"
@interface Decorator :Components{
@protected Components *components;
}
-(void)SetComponents:(Components*)component;
@end
Decorator類實(shí)現(xiàn)文件
#import"Decorator.h"
@implementation Decorator
-(void)SetComponents:(Components*)component{
components = component;
}
-(void)Operation{
if(components!=nil){
[components Operation];
}
}
@end
ConcreteComponent類接口文件
#import"Components.h"
@interface ConcreteComponent :Components
@end
ConcreteComponent類實(shí)現(xiàn)文件
#import "ConcreteComponent.h"
@implementation ConcreteComponent
-(void)Operation{
NSLog(@"具體操作的對(duì)象");
}
@end
ConcreteDecoratorA類接口文件
#import "ConcreteDecoratorA.h"
#import "Decorator.h"
@interface ConcreteDecoratorA :Decorator
@end
ConcreteDecoratorA類實(shí)現(xiàn)文件
#import"ConcreteDecoratorA.h"
@implementation ConcreteDecoratorA
-(void)Operation{
NSLog(@"具體裝飾對(duì)象A的操作");
[super Operation];
}
@end
ConcreteDecoratorB類接口文件
#import "Decorator.h"
@interface ConcreteDecoratorB :Decorator
@end
ConcreteDecoratorB類實(shí)現(xiàn)文件
#import "ConcreteDecoratorB.h"
@implementation ConcreteDecoratorB
-(void)Operation{
NSLog(@"具體裝飾對(duì)象B的操作");
[super Operation];
}
@end
Main方法
#import <Foundation/Foundation.h>
#import "ConcreteComponent.h"
#import "ConcreteDecoratorA.h"
#import "ConcreteDecoratorB.h"
int main (int argc,const char* argv[])
{
@autoreleasepool{
ConcreteComponent *c = [[ConcreteComponent alloc]init];
ConcreteDecoratorA *d1 = [[ConcreteDecoratorA alloc]init];
ConcreteDecoratorB *d2 = [[ConcreteDecoratorB alloc]init];
[d1 SetComponents:c];
[d2 SetComponents:d1];
[d2 Operation];
}
return 0;
}
好啦,上面是需要展示的類,語(yǔ)法上都很簡(jiǎn)單,沒(méi)有什么需要重點(diǎn)說(shuō)的,可能值得一提的是關(guān)于子類調(diào)用父類方法的知識(shí)點(diǎn),就是在調(diào)用每個(gè)對(duì)象的Operation方法的時(shí)候,里面會(huì)有一句代碼是[super Operation];這句代碼構(gòu)很關(guān)鍵,他構(gòu)成了各個(gè)對(duì)象之間Operation方法的跳轉(zhuǎn),以此完成對(duì)Components類對(duì)象的”裝飾”。
二、分類(Category)和委托(Delegation)
在 Object-C 里有兩個(gè)種非常常見(jiàn)的實(shí)現(xiàn)模式:分類(Category)和委托(Delegation)。
1.分類 Category
分類是一種非常強(qiáng)大的機(jī)制,它允許你在一個(gè)已存在的類里添加新方法,而不需要去為他添加一個(gè)子類。新方法在編譯的時(shí)候添加,它能像這個(gè)類的擴(kuò)展方法一樣正常執(zhí)行。一個(gè)裝飾器跟類的定義稍微有點(diǎn)不同的就是,因?yàn)檠b飾器不能被實(shí)例化,它只是一個(gè)擴(kuò)展。
提示:除了你自己類的擴(kuò)展,你還可在任何 Cocoa 類里的擴(kuò)展添加方法。
如何使用分類:
現(xiàn)在你有一個(gè) Album 對(duì)象,你需要把它顯示在一個(gè)表單視圖里(table view):
專輯的標(biāo)題從哪里來(lái)?Album 只是一個(gè)模型對(duì)象,它才不會(huì)去關(guān)心你如果去顯示這些數(shù)據(jù)。為了這些,你需要給 Album 類添加一些額外的代碼,但是請(qǐng)不要直接修改這個(gè)類。
你現(xiàn)在就需要為 Album 添加一個(gè)分類 (category) 的擴(kuò)展;它將定義一個(gè)新地方法用來(lái)返回一個(gè)數(shù)據(jù)結(jié)構(gòu),這個(gè)數(shù)據(jù)結(jié)構(gòu)可以很容易的被 UITableViews 使用。
這個(gè)數(shù)據(jù)結(jié)構(gòu)看起來(lái)如下:
為 Album 添加一個(gè)分類,導(dǎo)航 File\New\File… 選擇 Object-C category 模版─不要習(xí)慣的去選擇 Object-C class,在 Category 后面輸入 TableRepresentation,Category to 后面輸入 Album。
提示:你有沒(méi)有注意這個(gè)新文件的名字?Album+TableRepresentation 說(shuō)明它是 Album 類的一個(gè)擴(kuò)展。這個(gè)習(xí)慣很重要,因?yàn)榈谝贿@很容易讀,第二防止你或者其他人創(chuàng)建的分類跟其沖突。
打開(kāi) Album+TableRepresentation,加入下面的方法原型:
- (NSDictionary*)tr_tableRepresentation;
注意,這是一個(gè) tr_ 開(kāi)頭的方法名,就像是這個(gè)分類名字的縮寫一樣:TableRepresentation。其次,這個(gè)習(xí)慣會(huì)避免這個(gè)方法跟其它方法重名!
提示:如果分類 (Category) 聲明的一個(gè)方法跟原始類的一個(gè)方法重名,或者跟同類里的的另一個(gè)分類名字重復(fù)(或者是它的父類),當(dāng)它在運(yùn)行的時(shí)候,它就不知道要執(zhí)行哪個(gè)方法。如果是在你自己類的分類里,它不太可能出現(xiàn)大的問(wèn)題,但是如果一個(gè)標(biāo)準(zhǔn) Cocoa 或者 Cocoa Touch 類里面添加這個(gè)分類的方法,就可能會(huì)引起嚴(yán)重的問(wèn)題。
打開(kāi) Album+TableRepersentation.m 文件添加下面的方法:
- (NSDictionary*)tr_TableRepersentation
{
return @{@"titles":@[@"Artist", @"Album", @"Genre", @"Year"],
@"values":@[self.artist, self.title, self.genre, self.year]};
};
考慮一會(huì),為什么這種模式如些強(qiáng)大:
你能夠直接使用 Album 的屬性。
你已經(jīng)添加在 Album 類里,但它并不是它的子類。如果子類需要,你同樣也可以這樣做。
這樣一個(gè)簡(jiǎn)單的添加,Album 類的數(shù)據(jù)返回一個(gè) UITableView 可用的數(shù)據(jù)結(jié)構(gòu),但并不需要修改 Album 的代碼。
蘋果在基礎(chǔ)類里大量的使用了分類設(shè)計(jì)模式。去看看他們是怎么做的,打開(kāi) NSString.h。找到 @interface NSString,你將會(huì)看到這個(gè)類定義了三個(gè)分類:NSStringExtensionMethods, NSExtendedStringPropertyListParsing 和 NSStingDeprecated。在代碼片里,分類將幫助你保持方法的組織性和分離必。
2.委托 Delegation
另外一種裝飾器的設(shè)計(jì)模式是,委托 (Delegation),它是一種機(jī)制,一個(gè)對(duì)象代表另外一個(gè)對(duì)象或者其相互合作。例子,當(dāng)你使用 UITableView 的時(shí)候,其中一個(gè)方法是你必需要執(zhí)行的,tableView:numberOfRowsInSection:。
你可能并不期望 UITableView 知道每個(gè) section 中有多少行,這是程序的特性。因此,計(jì)算每個(gè) section 有多少行的工作就交給了 UITableView 的委托 (delegate)。它允許 UITableView 類不依賴它顯示的數(shù)據(jù)。
當(dāng)你創(chuàng)建了一個(gè)新的 UITableView 的時(shí)候,這里有一個(gè)類似的解釋:
UITableView 對(duì)象的工作就是顯示一個(gè)表單視圖。然而,最終它都需要一些它信息,它并不擁有這些信息。然后,它會(huì)轉(zhuǎn)向它的委托,發(fā)送一個(gè)添加信息的消息。在 Object-C 中實(shí)現(xiàn)委托模式,一個(gè)類可以通過(guò)協(xié)議 (protocol) 來(lái)聲明一個(gè)可選和必選的方法。稍后,在這個(gè)教程你將覆蓋一個(gè)協(xié)議 (protocols)。
它看起來(lái)比子類更容易,覆蓋需要的方法,但是考慮如果是單類的話你只能創(chuàng)建子類。如果你想一個(gè)對(duì)象委托兩個(gè)或者多個(gè)對(duì)象的時(shí)候,子類化的方法是不能實(shí)現(xiàn)的。
提示:這是一個(gè)很重要的模式。蘋果在 UIKit 類中大量的使用了此方法:UITableView, UITextView, UITextField, UIWebView, UIAlert, UIActionSheet, UICollectionView, UIPickerView, UIGestureRecognizer, UIScrollView。這個(gè)列表還可以有很多。
如何使用委托模式:
打開(kāi) ViewController.m,在頂部引入如下文件
#import "LibraryAPI.h"
#import "Album+TableRepresentation.h"
現(xiàn)在,在類的擴(kuò)展里的添加一些私有變量,它們看起來(lái)如下:
@interface ViewController (){
UITableView *dataTable;
NSArray *allAlbums;
NSDictionary *currentAlbumData;
int currentAlbumIndex;
}
@end
現(xiàn)在,替換類擴(kuò)展里的 @interface 這一行,完成后如下:
@interface ViewController () <UITableViewDataSoure, UITableViewDelegate> {
這就是如何設(shè)置一個(gè)正確的委托─把它相象成允許一個(gè)委托來(lái)履行一個(gè)方法的合同。這里,表明 ViewController 將會(huì)遵照 UITableViewDataSource 和 UITableViewDelegate 協(xié)議。這種方法下 UITableView 必須執(zhí)行它自己的委托方法。
下面,用下面的代碼替換 viewDidLoad:
- (void)viewDidLoad {
[super viewDidLoad];
// 1
self.view.backgroundColor = [UIColor colorWithRed:0.76f green:0.81f blue:0.87f alpha:1];
currentAlbumIndex = 0;
//2
allAlbums = [[LibraryAPI sharedInstance] getAlbums];
// 3
// the uitableview that presents the album data
dataTable = [[UITableView alloc] initWithFrame:CGRectMake(0, 120, self.view.frame.size.width, self.view.frame.size.height-120) style:UITableViewStyleGrouped];
dataTable.delegate = self;
dataTable.dataSource = self;
dataTable.backgroundView = nil;
[self.view addSubview:dataTable];
}
這里分析下上面的代碼:
把背景色改為漂亮的深藍(lán)色。
從 API 獲取一個(gè)列表,它包含所有的專輯數(shù)據(jù)。不能直接使用 PersistencyManager。
創(chuàng)建一個(gè) UITableView。你聲明了視圖控制器是 UITableView delegate/data source;因此,UITableView 將會(huì)提供視圖控制器需要的所有信息。
現(xiàn)在,在 ViewController.m 里面添加如下方法:
- (void)showDataForAlbumAtIndex:(int)albumIndex{
// defensive code: make sure the requested index is lower than the amount of albums
if (albumIndex < allAlbums.count) {
// fetch the album
Album *album = allAlbums[albumIndex];
// save the albums data to present it later in the tableview
currentAlbumData = [album tr_tableRepresentation];
} else {
currentAlbumData = nil;
}
// we have the data we need, let's refresh our tableview
[dataTable reloaddata];
}
showDataForAlbumAtIndex: 從專輯數(shù)組中取出需要的專輯數(shù)據(jù)。當(dāng)你需要顯示新數(shù)據(jù)的時(shí)候,你只需要重載數(shù)據(jù) (relaodData)。這是因?yàn)?UITableView 需要請(qǐng)求它的委托代理,像有多少 sections 將會(huì)在表單視圖中顯示,每個(gè) section 中有多少行,每行看起來(lái)是什么樣的。
在 viewDidLoad 中添加下面代碼
[self showDataForAlbumAtIndex:currentAlbumIndex];
當(dāng)程序運(yùn)行的時(shí)候它會(huì)加載當(dāng)前的專輯信息。由于 currentAlbumIndex 的預(yù)設(shè)值為 0,所以會(huì)顯示收藏中的第一張專輯信息。
構(gòu)建并運(yùn)行你的項(xiàng)目,你的程序會(huì)崩潰掉,在控制臺(tái)會(huì)輸入如下的異常:
出現(xiàn)什么問(wèn)題了?你已經(jīng)聲明了 ViewController 中的 UItableView 的委托(delegate)和數(shù)據(jù)源(data source)。但是在這種情況下,你必需執(zhí)行所有的必需方法─包含 tableView:numberOfRowsInsection:─你現(xiàn)在還沒(méi)有它。
在 ViewContrller.m 的 @implementation 和 @end 的任何地方添加如下代碼:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [currentAlbumData[@"titles"] count];
}
- (UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell"];
if (!cell) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:@"cell"];
}
cell.textLabel.text = currentAlbumData[@"titles"][indexPath.row];
cell.detailTextLabel.text = currentAlbumData[@"values"][indexPath.row];
return cell;
}
tableView:numberOfRowsIndexSection: 返回表單視圖顯示的行數(shù),匹配數(shù)據(jù)結(jié)構(gòu)中標(biāo)題的數(shù)目。
tableView:cellForRowAtIndexPath: 創(chuàng)建并返回一個(gè)帶標(biāo)題和信息的 cell。
現(xiàn)在構(gòu)建并運(yùn)行你的項(xiàng)目。你的程序開(kāi)始運(yùn)行并顯示出下圖的界面:
這目前為止事情看起來(lái)很不錯(cuò)。但是如果你回過(guò)去看第一張圖片的時(shí)候,你會(huì)發(fā)現(xiàn)在屏幕的頂端有一個(gè)可以水平滾動(dòng)的視圖,用于切換專輯。它只是簡(jiǎn)單的水平滾動(dòng),為什么不做一個(gè)可以重復(fù)使用的視圖來(lái)代替它呢。
- iOS App開(kāi)發(fā)中使用設(shè)計(jì)模式中的單例模式的實(shí)例解析
- 詳解iOS應(yīng)用的設(shè)計(jì)模式開(kāi)發(fā)中Mediator中介者模式的使用
- iOS App設(shè)計(jì)模式開(kāi)發(fā)中對(duì)迭代器模式的使用示例
- iOS App的設(shè)計(jì)模式開(kāi)發(fā)中對(duì)State狀態(tài)模式的運(yùn)用
- 實(shí)例解析設(shè)計(jì)模式中的外觀模式在iOS App開(kāi)發(fā)中的運(yùn)用
- iOS應(yīng)用運(yùn)用設(shè)計(jì)模式中的Strategy策略模式的開(kāi)發(fā)實(shí)例
- iOS App設(shè)計(jì)模式開(kāi)發(fā)中策略模式的實(shí)現(xiàn)示例
- IOS開(kāi)發(fā)中的設(shè)計(jì)模式匯總
相關(guān)文章
iOS開(kāi)發(fā)實(shí)現(xiàn)圖片瀏覽功能
這篇文章主要為大家詳細(xì)介紹了iOS開(kāi)發(fā)實(shí)現(xiàn)圖片瀏覽功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-01-01iOS開(kāi)發(fā)中使用文字圖標(biāo)iconfont的應(yīng)用示例
這篇文章主要介紹了iOS開(kāi)發(fā)中使用文字圖標(biāo)iconfont的應(yīng)用示例,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-10-10解析iOS內(nèi)存不足時(shí)的警告以及處理過(guò)程
這篇文章主要介紹了iOS內(nèi)存不足時(shí)的警告以及處理過(guò)程,包括View Controller和生命周期等相關(guān)方面的知識(shí),需要的朋友可以參考下2015-10-10iOS通過(guò)攝像頭圖像識(shí)別技術(shù)分享
本篇文章給大家詳細(xì)講述了讓IOS開(kāi)發(fā)中通過(guò)攝像頭進(jìn)行圖像識(shí)別的相關(guān)技術(shù),對(duì)此有興趣的朋友參考學(xué)習(xí)下吧。2018-02-02簡(jiǎn)單介紹iOS開(kāi)發(fā)中關(guān)于category的應(yīng)用
這篇文章主要介紹了iOS開(kāi)發(fā)中關(guān)于category的應(yīng)用,代碼仍然基于傳統(tǒng)的Objective-C,需要的朋友可以參考下2015-09-09