Objective-C编程之道 iOS设计模式解析
iOS设计模式解析-工厂模式
iOS设计模式解析-抽象工厂模式
iOS设计模式解析-外观模式
iOS设计模式解析-中介者模式
iOS设计模式解析-观察者模式
iOS设计模式解析-装饰模式
iOS设计模式解析-责任链模式
iOS设计模式解析-模板方法
iOS设计模式解析-策略模式
iOS设计模式解析-享元模式
iOS设计模式解析-代码地址
装饰模式:动态地给一个对象添加一些额外的职责。就扩展功能来说,装饰模式相比生成子类更为灵活。
何时使用装饰模式
在以下情形,自然会考虑使用这一模式。
- 想要在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责。
- 想要扩展一个类的行为,却做不到。类定义可能被隐藏,无法进行子类化;或者,对类的每个行为的扩展,为支持每种功能组合,将产生大量的子类。
- 对类的职责的扩展是可选的。
改变对象的“外表”和“内容”
“外表”变更(装饰模式)
- 从外部变更
- 每个节点不知道变更
“内容”变更(策略模式)
- 从内部变更
- 每个节点知道一组预定义的变更方式
下面将为UIImage创建图像滤镜来说明装饰模式
装饰模式是向对象添加新的行为与职责的一种方式,它不改变任何现有行为与接口。比如说有个图像对象,它只包含让客户端管理其属性的接口,仅此而已。我们想向它添加点花哨的东西,比如变换滤镜,但不想修改图像对象的现有接口。我们能做的是,再定义一个跟这个图像对象一样的类,但它包含对另一个图像对象的引用,增加这个图像对象的行为。新的类有一个用绘图上下文绘制自身的方法。在这个绘图方法中,它向内嵌的图像引用应用变换算法,绘制整个图像,然后返回结果图像。可以把这个过程想象为在图片上放一层玻璃。图片不用去管玻璃,而且我们看它的时候,还管它叫图片。玻璃本身可以有颜色,在表面有波纹,或者其他别的可以让图片看上去不同的东西。以后如果想向图像应用另一层滤镜,可以再定义一个滤镜类,就像变换用的滤镜那样,通过这个类,可以应用同样的机制来向图像增加自己的操作。变换滤镜之外的其他滤镜,可以获取结果图像,并继续处理。但有一点,在装饰滤镜的流水线上传递的图像,不必是原来的那个,但必须是同样的类型。因此从变换滤镜返回的图像是一幅变换后的图像。然后当它被传给颜色滤镜的时候,返回的图像就是一幅经过着色的变换后的图像。
在这里,具体组件是一个UIImage类型,但我们不想只是为了让它参与进来就继承它,因此我们为它定义了一个分类。UIImage( ImageComponent)分类采用了ImageComponent协议。因为协议中定义的方法在UIImage中已经都有了,所以不必在分类中进行实现。
ImageTransformFilter和ImageShadowFilter专注于通过重载apply方法提供自己的滤镜算法。它们继承抽象的ImageFilter基类,ImageFilter里有一个对ImageComponent的引用,即私有成员变量component_。
ImageComponent
#import <UIKit/UIKit.h>
@protocol ImageComponent <NSObject>
//将截获这些UIImage的方法,插入附加的行为
@optional
- (void) drawAsPatternInRect:(CGRect)rect;
- (void) drawAtPoint:(CGPoint)point;
- (void) drawAtPoint:(CGPoint)point blendMode:(CGBlendMode)blendMode alpha:(CGFloat)alpha;
- (void) drawInRect:(CGRect)rect;
- (void) drawInRect:(CGRect)rect blendMode:(CGBlendMode)blendMode alpha:(CGFloat)alpha;
@end
全部的draw*方法都声明为@optional,告诉编译器,在找不到这些方法的对应实现时不要报错。
UIImage (ImageComponent)
#import <Foundation/Foundation.h>
#import "ImageComponent.h"
@interface UIImage (ImageComponent) <ImageComponent>
@end
#import "UIImage+ImageComponent.h"
/*
@implementation UIImage (ImageComponent)
@end
*/
声明了UIImage的分类,遵守ImageComponent协议,但完全没有实际的实现。
ImageFilter
#import <Foundation/Foundation.h>
#import "ImageComponent.h"
#import "UIImage+ImageComponent.h"
@interface ImageFilter : NSObject <ImageComponent>
{
@private
id <ImageComponent> component_;
}
@property (nonatomic, retain) id <ImageComponent> component;
- (void) apply;
- (id) initWithImageComponent:(id <ImageComponent>) component;
- (id) forwardingTargetForSelector:(SEL)aSelector;
/*
// overridden methods in UIImage APIs
- (void) drawAsPatternInRect:(CGRect)rect;
- (void) drawAtPoint:(CGPoint)point;
- (void) drawAtPoint:(CGPoint)point blendMode:(CGBlendMode)blendMode alpha:(CGFloat)alpha;
- (void) drawInRect:(CGRect)rect;
- (void) drawInRect:(CGRect)rect blendMode:(CGBlendMode)blendMode alpha:(CGFloat)alpha;
*/
@end
#import "ImageFilter.h"
@implementation ImageFilter
@synthesize component=component_;
- (id) initWithImageComponent:(id <ImageComponent>) component
{
if (self = [super init])
{
// save an ImageComponent
[self setComponent:component];
}
return self;
}
- (void) apply
{
// should be overridden by subclasses
// to apply real filters
}
- (id) forwardingTargetForSelector:(SEL)aSelector
{
NSString *selectorName = NSStringFromSelector(aSelector);
if ([selectorName hasPrefix:@"draw"])
{
[self apply];
}
return component_;
}
/*
- (void) drawAsPatternInRect:(CGRect)rect
{
[self apply];
[component_ drawAsPatternInRect:rect];
}
- (void) drawAtPoint:(CGPoint)point
{
[self apply];
[component_ drawAtPoint:point];
}
- (void) drawAtPoint:(CGPoint)point blendMode:(CGBlendMode)blendMode alpha:(CGFloat)alpha
{
[self apply];
[component_ drawAtPoint:point
blendMode:blendMode
alpha:alpha];
}
- (void) drawInRect:(CGRect)rect
{
[self apply];
[component_ drawInRect:rect];
}
- (void) drawInRect:(CGRect)rect blendMode:(CGBlendMode)blendMode alpha:(CGFloat)alpha
{
[self apply];
[component_ drawInRect:rect
blendMode:blendMode
alpha:alpha];
}
*/
@end
在initwithImageComponent:方法中,没做太多的事情,只是把参数的ImageComponent引用赋值给自己。而且,它的apply方法什么也没做,直到在具体滤镜类中重载。有趣的是,使用forwardingTargetForSelector:截获的消息调用,ImageFilter的实例
却不知如何响应。这个方法让子类把替代的接收器转发给运行库,使原始消息得到转发。但我们只关心以@"draw"开头的方法,然后通过返回component_,把剩下的事情都直接转给
component_。 比如,当消息drawAtRect :被发到ImageFilter的实例时,它将会被forwarding-TargetForselector:方法截获,等待替代的接收器,因为ImageFilter并没有实现它。由于消息以"draw"开头,它会在component_处理消息之前,先向自己发一个apply消息来做些处理。
ImageTransformFilter
#import <Foundation/Foundation.h>
#import "ImageFilter.h"
@interface ImageTransformFilter : ImageFilter
{
@private
CGAffineTransform transform_;
}
@property (nonatomic, assign) CGAffineTransform transform;
- (id) initWithImageComponent:(id <ImageComponent>)component
transform:(CGAffineTransform)transform;
- (void) apply;
@end
#import "ImageTransformFilter.h"
@implementation ImageTransformFilter
@synthesize transform=transform_;
- (id) initWithImageComponent:(id <ImageComponent>)component
transform:(CGAffineTransform)transform
{
if (self = [super initWithImageComponent:component])
{
[self setTransform:transform];
}
return self;
}
- (void) apply
{
CGContextRef context = UIGraphicsGetCurrentContext();
// setup transformation
CGContextConcatCTM(context, transform_);
}
@end
ImageShadowFilter
#import <Foundation/Foundation.h>
#import "ImageFilter.h"
@interface ImageShadowFilter : ImageFilter
- (void) apply;
@end
#import "ImageShadowFilter.h"
@implementation ImageShadowFilter
- (void) apply
{
CGContextRef context = UIGraphicsGetCurrentContext();
// set up shadow
CGSize offset = CGSizeMake (-25, 15);
CGContextSetShadow(context, offset, 20.0);
}
@end
调用
- (void)viewDidLoad {
[super viewDidLoad];
// load the original image
UIImage *image = [UIImage imageNamed:@"Image.png"];
// create a transformation
CGAffineTransform rotateTransform = CGAffineTransformMakeRotation(-M_PI / 4.0);
CGAffineTransform translateTransform = CGAffineTransformMakeTranslation(-image.size.width / 2.0,
image.size.height / 8.0);
CGAffineTransform finalTransform = CGAffineTransformConcat(rotateTransform, translateTransform);
// a true subclass approach
id <ImageComponent> transformedImage = [[ImageTransformFilter alloc] initWithImageComponent:image
transform:finalTransform];
id <ImageComponent> finalImage = [[ImageShadowFilter alloc] initWithImageComponent:transformedImage];
// create a new image view
// with a filtered image
DecoratorView *decoratorView = [[DecoratorView alloc] initWithFrame:[self.view bounds]];
[decoratorView setImage:finalImage];
[self.view addSubview:decoratorView];
}
总结
真正子类方式的实现使用一种较为结构化的方式连接各种装饰器。分类的方式更为简单和轻量,适用于现有类只需要少量装饰器的应用程序(此文章没有讲解)。虽然分类不同于实际的子类化,不能严格实现模式的原始风格,但它实现了解决同样问题的意图。设计图像滤镜处理示例程序那样的应用时,装饰模式是自然而然的选择。图像滤镜的任何组合都能动态应用或删除,而不影响UIImage原有行为的完整性。
网友评论