深入解析设计模式中的装饰器模式在iOS应用开发中的实现

装饰器模式可以在不修改代码的情况下灵活的为一对象添加行为和职责。当你要修改一个被其它类包含的类的行为时,它可以代替子类化方法。

一、基本实现
下面我把类的结构图向大家展示如下:

让我们简单分析一下上面的结构图,Component是定义一个对象接口,可以给这些对象动态地添加职责。ConcreteComponent是定义了一个具体的对象,也可以给这个对象添加一些职责。Decorator,装饰抽象类,继承了Component,从外类来扩展Component类的功能,但对于Component来说,是无需知道Decorator的存在的。至于ConcreteDecorator就是具体的装饰对象,起到给Component添加职责的功能。

下面,还是老套路,我会尽可能的给出Objective C实现的最简单的实例代码,首先声明一下,这些代码是运行在ARC环境下的,所以对于某些可能引起内存泄漏的资源并没有采用手动释放的方式,这一点还是需要大家注意。

注意:本文所有代码均在ARC环境下编译通过。

Components类接口文件

代码如下:

#import<Foundation/Foundation.h>

@interface Components :NSObject
-(void) Operation;
@end

Components类实现文件

#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类实现文件

#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类实现文件

代码如下:

#import "ConcreteComponent.h"

@implementation ConcreteComponent
-(void)Operation{
    NSLog(@"具体操作的对象");
}
@end

ConcreteDecoratorA类接口文件

代码如下:

#import "ConcreteDecoratorA.h"
#import "Decorator.h"

@interface ConcreteDecoratorA :Decorator
@end

ConcreteDecoratorA类实现文件

#import"ConcreteDecoratorA.h"

代码如下:

@implementation ConcreteDecoratorA
-(void)Operation{
    NSLog(@"具体装饰对象A的操作");
    [super Operation];
}
@end

ConcreteDecoratorB类接口文件

#import "Decorator.h"

代码如下:

@interface ConcreteDecoratorB :Decorator
@end

ConcreteDecoratorB类实现文件

代码如下:

#import "ConcreteDecoratorB.h"

@implementation ConcreteDecoratorB
-(void)Operation{
    NSLog(@"具体装饰对象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;
}

好啦,上面是需要展示的类,语法上都很简单,没有什么需要重点说的,可能值得一提的是关于子类调用父类方法的知识点,就是在调用每个对象的Operation方法的时候,里面会有一句代码是[super Operation];这句代码构很关键,他构成了各个对象之间Operation方法的跳转,以此完成对Components类对象的”装饰”。

二、分类(Category)和委托(Delegation)
在 Object-C 里有两个种非常常见的实现模式:分类(Category)和委托(Delegation)。

1.分类 Category

分类是一种非常强大的机制,它允许你在一个已存在的类里添加新方法,而不需要去为他添加一个子类。新方法在编译的时候添加,它能像这个类的扩展方法一样正常执行。一个装饰器跟类的定义稍微有点不同的就是,因为装饰器不能被实例化,它只是一个扩展。

提示:除了你自己类的扩展,你还可在任何 Cocoa 类里的扩展添加方法。
如何使用分类:

现在你有一个 Album 对象,你需要把它显示在一个表单视图里(table view):

专辑的标题从哪里来?Album 只是一个模型对象,它才不会去关心你如果去显示这些数据。为了这些,你需要给 Album 类添加一些额外的代码,但是请不要直接修改这个类。

你现在就需要为 Album 添加一个分类 (category) 的扩展;它将定义一个新地方法用来返回一个数据结构,这个数据结构可以很容易的被 UITableViews 使用。

这个数据结构看起来如下:

为 Album 添加一个分类,导航 File\New\File… 选择 Object-C category 模版─不要习惯的去选择 Object-C class,在 Category 后面输入 TableRepresentation,Category to 后面输入 Album。

提示:你有没有注意这个新文件的名字?Album+TableRepresentation 说明它是 Album 类的一个扩展。这个习惯很重要,因为第一这很容易读,第二防止你或者其他人创建的分类跟其冲突。
打开 Album+TableRepresentation,加入下面的方法原型:

- (NSDictionary*)tr_tableRepresentation;
注意,这是一个 tr_ 开头的方法名,就像是这个分类名字的缩写一样:TableRepresentation。其次,这个习惯会避免这个方法跟其它方法重名!

提示:如果分类 (Category) 声明的一个方法跟原始类的一个方法重名,或者跟同类里的的另一个分类名字重复(或者是它的父类),当它在运行的时候,它就不知道要执行哪个方法。如果是在你自己类的分类里,它不太可能出现大的问题,但是如果一个标准 Cocoa 或者 Cocoa Touch 类里面添加这个分类的方法,就可能会引起严重的问题。
打开 Album+TableRepersentation.m 文件添加下面的方法:

代码如下:

- (NSDictionary*)tr_TableRepersentation
{
    return @{@"titles":@[@"Artist", @"Album", @"Genre", @"Year"],
            @"values":@[self.artist, self.title, self.genre, self.year]};
};

考虑一会,为什么这种模式如些强大:

你能够直接使用 Album 的属性。
你已经添加在 Album 类里,但它并不是它的子类。如果子类需要,你同样也可以这样做。
这样一个简单的添加,Album 类的数据返回一个 UITableView 可用的数据结构,但并不需要修改 Album 的代码。
苹果在基础类里大量的使用了分类设计模式。去看看他们是怎么做的,打开 NSString.h。找到 @interface NSString,你将会看到这个类定义了三个分类:NSStringExtensionMethods, NSExtendedStringPropertyListParsing 和 NSStingDeprecated。在代码片里,分类将帮助你保持方法的组织性和分离必。

2.委托 Delegation

另外一种装饰器的设计模式是,委托 (Delegation),它是一种机制,一个对象代表另外一个对象或者其相互合作。例子,当你使用 UITableView 的时候,其中一个方法是你必需要执行的,tableView:numberOfRowsInSection:。

你可能并不期望 UITableView 知道每个 section 中有多少行,这是程序的特性。因此,计算每个 section 有多少行的工作就交给了 UITableView 的委托 (delegate)。它允许 UITableView 类不依赖它显示的数据。

当你创建了一个新的 UITableView 的时候,这里有一个类似的解释:

UITableView 对象的工作就是显示一个表单视图。然而,最终它都需要一些它信息,它并不拥有这些信息。然后,它会转向它的委托,发送一个添加信息的消息。在 Object-C 中实现委托模式,一个类可以通过协议 (protocol) 来声明一个可选和必选的方法。稍后,在这个教程你将覆盖一个协议 (protocols)。

它看起来比子类更容易,覆盖需要的方法,但是考虑如果是单类的话你只能创建子类。如果你想一个对象委托两个或者多个对象的时候,子类化的方法是不能实现的。

提示:这是一个很重要的模式。苹果在 UIKit 类中大量的使用了此方法:UITableView, UITextView, UITextField, UIWebView, UIAlert, UIActionSheet, UICollectionView, UIPickerView, UIGestureRecognizer, UIScrollView。这个列表还可以有很多。
如何使用委托模式:

打开 ViewController.m,在顶部引入如下文件

代码如下:

#import "LibraryAPI.h"
#import "Album+TableRepresentation.h"

现在,在类的扩展里的添加一些私有变量,它们看起来如下:

代码如下:

@interface ViewController (){
    UITableView *dataTable;
    NSArray *allAlbums;
    NSDictionary *currentAlbumData;
    int currentAlbumIndex;
}
@end

现在,替换类扩展里的 @interface 这一行,完成后如下:

代码如下:

@interface ViewController () <UITableViewDataSoure, UITableViewDelegate> {

这就是如何设置一个正确的委托─把它相象成允许一个委托来履行一个方法的合同。这里,表明 ViewController 将会遵照 UITableViewDataSource 和 UITableViewDelegate 协议。这种方法下 UITableView 必须执行它自己的委托方法。

下面,用下面的代码替换 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];
}

这里分析下上面的代码:

把背景色改为漂亮的深蓝色。
从 API 获取一个列表,它包含所有的专辑数据。不能直接使用 PersistencyManager。
创建一个 UITableView。你声明了视图控制器是 UITableView delegate/data source;因此,UITableView 将会提供视图控制器需要的所有信息。
现在,在 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: 从专辑数组中取出需要的专辑数据。当你需要显示新数据的时候,你只需要重载数据 (relaodData)。这是因为 UITableView 需要请求它的委托代理,像有多少 sections 将会在表单视图中显示,每个 section 中有多少行,每行看起来是什么样的。

在 viewDidLoad 中添加下面代码

代码如下:

[self showDataForAlbumAtIndex:currentAlbumIndex];

当程序运行的时候它会加载当前的专辑信息。由于 currentAlbumIndex 的预设值为 0,所以会显示收藏中的第一张专辑信息。

构建并运行你的项目,你的程序会崩溃掉,在控制台会输入如下的异常:

出现什么问题了?你已经声明了 ViewController 中的 UItableView 的委托(delegate)和数据源(data source)。但是在这种情况下,你必需执行所有的必需方法─包含 tableView:numberOfRowsInsection:─你现在还没有它。

在 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: 返回表单视图显示的行数,匹配数据结构中标题的数目。

tableView:cellForRowAtIndexPath: 创建并返回一个带标题和信息的 cell。

现在构建并运行你的项目。你的程序开始运行并显示出下图的界面:

这目前为止事情看起来很不错。但是如果你回过去看第一张图片的时候,你会发现在屏幕的顶端有一个可以水平滚动的视图,用于切换专辑。它只是简单的水平滚动,为什么不做一个可以重复使用的视图来代替它呢。

(0)

相关推荐

  • iOS App设计模式开发中策略模式的实现示例

    这次介绍一下策略模式(Strategy Pattern),相比之下是一种比较简单的模式.它也叫政策模式(Policy Pattern). 策略模式使用的就是面向对象的继承和多态机制,其他的没有什么玄机.策略模式适合使用在: 1. 多个类只有在算法或行为上稍有不同的场景. 2. 算法需要自由切换的场景. 3. 需要屏蔽算法规则的场景. 使用策略模式当然也有需要注意的地方,那么就是策略类不要太多,如果一个策略家族的具体策略数量超过4个,则需要考虑混合模式,解决策略类膨胀和对外暴露问题.在实际项目中,

  • IOS开发中的设计模式汇总

    iOS开发学习中,经常弄不清楚ios的开发模式,今天我们就来进行简单的总结和探讨~ (一)代理模式 应用场景:当一个类的某些功能需要由别的类来实现,但是又不确定具体会是哪个类实现. 优势:解耦合 敏捷原则:开放-封闭原则 实例:tableview的 数据源delegate,通过和protocol的配合,完成委托诉求. 列表row个数delegate 自定义的delegate (二)观察者模式 应用场景:一般为model层对,controller和view进行的通知方式,不关心谁去接收,只负责发布

  • iOS App设计模式开发中对迭代器模式的使用示例

    何为迭代器模式? 迭代器提供了一种顺序访问集合对象中元素的方法,而无需暴漏结构的底层表示和细节.遍历集合中元素的职能从集合本身转移到迭代器对象.迭代器定义了一个用于访问集合元素并记录当前元素的接口.不同的迭代器可以执行不同的策略. 例子 说了这么多,下面给大家展示一下类关系图. 上图中Client的右边是迭代器,左边是具体迭代的类型,在迭代器内部对具体需要迭代的类型进行了引用,还算不难理解吧,呵呵.其实,看起来是为了对具体类型进行解耦.好啦,下面给出具体的代码实现,简单的模拟了迭代器模式. 注意

  • iOS App的设计模式开发中对State状态模式的运用

    1.概述 在软件开发过程中,应用程序可能会根据不同的情况作出不同的处理.最直接的解决方案是将这些所有可能发生的情况全都考虑到.然后使用if... ellse语句来做状态判断来进行不同情况的处理.但是对复杂状态的判断就显得"力不从心了".随着增加新的状态或者修改一个状体(if else(或switch case)语句的增多或者修改)可能会引起很大的修改,而程序的可读性,扩展性也会变得很弱.维护也会很麻烦.那么我就考虑只修改自身状态的模式. 例子1:按钮来控制一个电梯的状态,一个电梯开们,

  • 实例解析设计模式中的外观模式在iOS App开发中的运用

    外观模式(Facade),为子系统中的一组接口提供一个一致的界面,此模式定义 一个高层接口,这个接口使得这一子系统更加容易使用. 下面给大家展示一下类的结构图,想必大家一看就明白了: 其实这个模式中,没有类与类之间的继承关系,只是进行了简单的类引用,统一了对外的接口而已.看起来是不是很简单?废话不多说了,下面简单向大家展示一下代码吧! 注意:本文所有代码均在ARC环境下编译通过. SubSystemOne类接口 复制代码 代码如下: #import <Foundation/Foundation.

  • iOS App开发中使用设计模式中的单例模式的实例解析

    一.单例的作用 顾名思义,单例,即是在整个项目中,这个类的对象只能被初始化一次.它的这种特性,可以广泛应用于某些需要全局共享的资源中,比如管理类,引擎类,也可以通过单例来实现传值.UIApplication.NSUserDefaults等都是IOS中的系统单例. 二.单例模式的两种写法 1,常用写法 #import "LGManagerCenter.h" static LGManagerCenter *managerCenter; @implementation LGManagerCe

  • iOS应用运用设计模式中的Strategy策略模式的开发实例

    在写程序的时候,我们经常会碰到这样的场景:把一堆算法塞到同一段代码中,然后使用if-else或switch-case条件语句来决定要使用哪个算法?这些算法可能是一堆相似的类函数或方法,用以解决相关的问题.比如,一个验证输入数据的例程,数据本身可以是任何数据类型(如NSString.CGFloat等),每种数据类型需要不同的验证算法.如果能把每个算法封装成一个对象,那么就能消除根据数据类型决定使用什么算法的一堆if-else或switch-case语句. 我们把相关算法分离为不同的类,称为策略模式

  • 详解iOS应用的设计模式开发中Mediator中介者模式的使用

    何为中介者模式? 面向对象的设计鼓励把行为分散到不同对象中,这种分散可能导致对象之间的相互关联.在最糟糕的情况下,所有对象都彼此了解并相互操作. 虽然把行为分散到不同对象增强了可复用性,但是增加的相互关联又减少了获得的益处.增加的关联使得对象很难或不能在不依赖其他对象的情况下工作.应用程序的整体行为可能难以进行任何重大修改,因为行为分布于许多对象.于是结果可能是创建越来越多的子类,以支持应用程序中的任何新行为. 中介者模式:用一个对象来封装一系列对象的交互方式.中介者使各对象不需要显式地相互引用

  • 深入解析设计模式中的装饰器模式在iOS应用开发中的实现

    装饰器模式可以在不修改代码的情况下灵活的为一对象添加行为和职责.当你要修改一个被其它类包含的类的行为时,它可以代替子类化方法. 一.基本实现 下面我把类的结构图向大家展示如下: 让我们简单分析一下上面的结构图,Component是定义一个对象接口,可以给这些对象动态地添加职责.ConcreteComponent是定义了一个具体的对象,也可以给这个对象添加一些职责.Decorator,装饰抽象类,继承了Component,从外类来扩展Component类的功能,但对于Component来说,是无需

  • Java结构性设计模式中的装饰器模式介绍使用

    目录 装饰器模式 概述 实现原理 主要角色 应用场景 优缺点 装饰器模式的基本使用 创建抽象组件 具体组件 抽象装饰器 具体装饰器 客户端调用 装饰器模式 概述 装饰器模式(Decorator Pattern)也称为包装模式(Wrapper Pattern),属于结构型模式. 它是指在不改变原有对象的基础之上,允许向一个现有的对象添加新的功能,同时又不改变其结构,作为现有的类的一个包装. 这种模式创建一个装饰类,用来包装原有的类,并在保持类方法签名完整性的前提下,提供了额外的功能. 提供了比继承

  • 实例讲解Ruby使用设计模式中的装饰器模式的方法

    概述        若你从事过面向对象开发,实现给一个类或对象增加行为,使用继承机制,这是所有面向对象语言的一  个基本特性.如果已经存在的一个类缺少某些方法,或者须要给方法添加更多的功能(魅力),你也许会仅仅继承这个类来产生一个新类-这建立在额外的代码上.       通过继承一个现有类可以使得子类在拥有自身方法的同时还拥有父类的方法.但是这种方法是静态的,用户不能控制增加行为的方式和时机.如果  你希望改变一个已经初始化的对象的行为,你怎么办?或者,你希望继承许多类的行为,改怎么办?前一个,

  • 设计模式开发中的备忘录模式在iOS应用开发中的运用实例

    何为备忘录模式? 在响应某些事件时,应用程序需要保存自身的状态,比如当用户保存文档或程序退出时.例如,游戏退出之前,可能需要保存当前会话的状态,如游戏等级.敌人数量.可用武器的种类等.游戏再次打开时,玩家可以从离开的地方接着玩.很多时候,保存程序的状态真的不需要什么特别巧妙的方法.任何简单有效的方法都可以,但是同时,保存信息应该只对原始程序有意义.原始程序应该是能够解码它所保存文档中的信息的唯一实体.这就是备忘录模式应用于游戏.文字处理等程序的软件设计中的方式,这些程序需要保存当前上下文的复杂状

  • 学习php设计模式 php实现装饰器模式(decorator)

    动态的给一个对象添加一些额外的职责.就增加功能来说,Decorator模式相比生成子类更为灵活[GOF95] 装饰模式是以对客户透明的方式动态地给一个对象附加上更多的职责.这也就是说,客户端并不会觉得对象在装饰前和装饰后有什么不同.装饰模式可以在不使用创造更多子类的情况下,将对象的功能加以扩展. 一.装饰模式结构图 二.装饰模式中主要角色 抽象构件(Component)角色:定义一个对象接口,以规范准备接收附加职责的对象,从而可以给这些对象动态地添加职责. 具体构件(Concrete Compo

  • 举例讲解设计模式中的原型模式在iOS应用开发中的作用

    1 前言 在许多面向对象的应用程序中,有些对象的创建代价过于大或者过于复杂.要是可以重建相同的对象并作轻微的改动,事情会容易许多.我们可以通过轻微的改动重用已有的对象,以适应程序中的特定情况.今天我们就来学习一下该模式. 2 详述 2.1 定义 应用于"复制"操作的模式成为原型(Prototype)模式.复制(cloning)指用同一模具生产一系列的产品.模具所基于的物品称为原型.尽管产品是用同一模具复制的,但是某些属性,如颜色与尺寸,可以稍有不同,但是他们还是属于同一类. 2.2 何

  • 实例讲解设计模式中的命令模式在iOS App开发中的运用

    命令模式封装一个请求或行为作为一个对象.封装的请求比原的更加灵活,可以在对象之间传递,储存,动态修改,或放入一个队列. 那么让我们简要的说一下命令模式的特点. 它能比较容易地设计一个命令队列: 在需要的情况下,可以较容易地将命令记入日志: 允许接收请求地一方决定是否要否决请求: 可以容易地实现对请求地撤销和重做: 由于加进新地具体命令类不影响其他的类,因此增加新的具体命令类很容易: 把请求一个操作的对象与知道怎么执行一个操作的对象分隔开. 下面给出基本的类结构图: 上面这张图是命令模式的类结构的

  • PHP设计模式之装饰器模式定义与用法详解

    本文实例讲述了PHP设计模式之装饰器模式定义与用法.分享给大家供大家参考,具体如下: 什么是装饰器模式 作为一种结构型模式, 装饰器(Decorator)模式就是对一个已有结构增加"装饰". 适配器模式, 是为现在有结构增加的是一个适配器类,.将一个类的接口,转换成客户期望的另外一个接口.适配器让原本接口不兼容的类可以很好的合作. 装饰器模式是将一个对象包装起来以增强新的行为和责任.装饰器也称为包装器(类似于适配器) 有些设计设计模式包含一个抽象类,而且该抽象类还继承了另一个抽象类,这

  • 详解Python中的装饰器、闭包和functools的教程

    装饰器(Decorators) 装饰器是这样一种设计模式:如果一个类希望添加其他类的一些功能,而不希望通过继承或是直接修改源代码实现,那么可以使用装饰器模式.简单来说Python中的装饰器就是指某些函数或其他可调用对象,以函数或类作为可选输入参数,然后返回函数或类的形式.通过这个在Python2.6版本中被新加入的特性可以用来实现装饰器设计模式. 顺便提一句,在继续阅读之前,如果你对Python中的闭包(Closure)概念不清楚,请查看本文结尾后的附录,如果没有闭包的相关概念,很难恰当的理解P

随机推荐