前言
通常,我们拍照的时候,不会去想将来如何装饰它。比方说,后来冲印了照片,决定把它放入一个特殊玻璃制成的精美相框中。以后也可以把同一张照片放在别的相框里。虽然换了相框,但是照片没有受到影响,因为只是往照片添加了东西,而并没有改变它。在面向对象软件中,我们借用了类似的思想,向对象添加行为,而不破坏其原有风格,因此增加了的对象是同一个类的加强版。我们把这个设计模式叫做装饰模式。
什么是装饰模式
装饰模式是指动态地给一个对象添加一些额外的职责,同时又不改变其结构。就扩展功能来说,装饰模式相比生成子类更为灵活。
标准的装饰模式中 Component
定义了一些抽象操作,其实体类将进行重载以实现自己特定的操作。抽象的 Component
类可被细化为另一个叫做 Decorator
的抽象类。 Decorator
包含了 另一个 Component
的引用,定义了扩展这个 Component
的实例的装饰性行为。 ConcreteDecorator
为其它 Component
或 Decorator
定义了几个扩展行为,并且会在自己的操作中执行内嵌的 Component
操作。
什么时候使用装饰模式
- 想要在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责
- 扩展一个类的功能,由于继承为类引入静态特征,并且随着扩展功能的增多,子类会很膨胀,在不想增加很多子类的情况下扩展类。需要动态增加功能,动态撤销。
装饰模式的优缺点
装饰模式的优点
装饰类和被装饰类可以独立发展,不会相互耦合,装饰模式是继承的一个替代模式,装饰模式可以动态扩展一个实现类的功能。
装饰模式的缺点
多层装饰比较复杂
Cocoa 中的装饰模式
范畴
Cocoa 框架本身利用范畴 (category)的方式实现了装饰模式,通过它可以向类添加行为,而不必进行子类化。通过范畴添加的方法对类原有的方法没有不良影响。范畴中的方法成为了类的一部分,并可由子类继承。然而这并不是一种严格的实现,它实现了模式的意图,但却是一种辩题。由范畴添加的行为是编译时绑定的,而且范畴实际上没有封装被扩展的类的实例。
装饰模式的实现
标准装饰模式的实现
通过装饰模式为图形创建滤镜处理,首先需要使用一种 Component
接口使得 UIImage
和这些滤镜连接起来,任何具体的 ImageComponent
和装饰器都可以处理这些调用。
@protocol ImageComponent
// We will intercept these
// UIImage methods and add
// additional behavior
@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
接着通过创建 UIImage
的范畴遵守 ImageComponent
,由于协议中定义的方法,UIImage
已经实现,所以不必再范畴中实现
@interface UIImage (ImageComponent)
@end
创建抽象的 ImageFilter
装饰类,ImageFilter
的 apply
方法让具体滤镜子类向 _component
增加额外行为,通过 forwardingTargetForSelector:
让子类返回代替的接收器处理 aSelector
@interface ImageFilter : NSObject
{
@private
id _component;
}
@property (nonatomic, strong) id component;
- (void) apply;
- (id) initWithImageComponent:(id ) 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
@implementation ImageFilter
@synthesize component=_component;
- (id) initWithImageComponent:(id ) 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
ImageTransformFilter
和 ImageShadowFilter
专注于通过重载 apply
方法提供自己的滤镜算法。
@interface ImageTransformFilter : ImageFilter
{
@private
CGAffineTransform _transform;
}
@property (nonatomic, assign) CGAffineTransform transform;
- (id) initWithImageComponent:(id )component
transform:(CGAffineTransform)transform;
- (void) apply;
@end
@implementation ImageTransformFilter
@synthesize transform=_transform;
- (id) initWithImageComponent:(id )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
@interface ImageShadowFilter : ImageFilter
{
}
- (void) apply;
@end
@implementation ImageShadowFilter
- (void) apply
{
CGContextRef context = UIGraphicsGetCurrentContext();
// set up shadow
CGSize offset = CGSizeMake (-25, 15);
CGContextSetShadow(context, offset, 20.0);
}
@end
创建一个 DecoratorView
,实现 Image
的绘制调用滤镜处理
@interface DecoratorView : UIView
{
@private
UIImage *_image;
}
@property (nonatomic, strong) UIImage *image;
@end
最后为图片增加滤镜
// 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 transformedImage = [[ImageTransformFilter alloc] initWithImageComponent:image
transform:finalTransform];
id finalImage = [[ImageShadowFilter alloc] initWithImageComponent:transformedImage];
DecoratorView *decoratorView = [[DecoratorView alloc] initWithFrame:[self.view bounds];
[decoratorView setImage:finalImage];
[self.view addSubview:decoratorView];
使用范畴的方式实现装饰
在使用范畴的方式中,只需要向 UIImage
类中添加滤镜,构成范畴,首先创建BaseFilter
,与 ImageFilter
类似定义几个基本的二维绘图操作。
@interface UIImage (BaseFilter)
- (CGContextRef) beginContext;
- (UIImage *) getImageFromCurrentImageContext;
- (void) endContext;
@end
@implementation UIImage (BaseFilter)
- (CGContextRef) beginContext
{
// Create a graphics context with the target size
// On iOS 4 and later, use UIGraphicsBeginImageContextWithOptions
// to take the scale into consideration
// On iOS prior to 4, fall back to use UIGraphicsBeginImageContext
CGSize size = [self size];
if (NULL != UIGraphicsBeginImageContextWithOptions)
UIGraphicsBeginImageContextWithOptions(size, NO, 0);
else
UIGraphicsBeginImageContext(size);
CGContextRef context = UIGraphicsGetCurrentContext();
return context;
}
- (UIImage *) getImageFromCurrentImageContext
{
[self drawAtPoint:CGPointZero];
// Retrieve the UIImage from the current context
UIImage *imageOut = UIGraphicsGetImageFromCurrentImageContext();
return imageOut;
}
- (void) endContext
{
UIGraphicsEndImageContext();
}
@end
创建 Transform
和 Shadow
范畴实现扩展行为
@interface UIImage (Transform)
- (UIImage *) imageWithTransform:(CGAffineTransform)transform;
@end
@implementation UIImage (Transform)
- (UIImage *) imageWithTransform:(CGAffineTransform)transform
{
CGContextRef context = [self beginContext];
// setup transformation
CGContextConcatCTM(context, transform);
// Draw the original image to the context
UIImage *imageOut = [self getImageFromCurrentImageContext];
[self endContext];
return imageOut;
}
@end
@interface UIImage (Shadow)
- (UIImage *) imageWithDropShadow;
@end
- (UIImage *) imageWithDropShadow
{
CGContextRef context = [self beginContext];
// set up shadow
CGSize offset = CGSizeMake (-25, 15);
CGContextSetShadow(context, offset, 20.0);
// Draw the original image to the context
UIImage * imageOut = [self getImageFromCurrentImageContext];
[self endContext];
return imageOut;
}
@end
总结
装饰模式动态地为对象附加额外的职责,为扩展功能提供了一种灵活的替代子类的方法。与子类化一样,装饰模式的改编允许在不修改现有代码的情况下合并新行为。装饰模式包装其行为扩展的类的对象。它们实现与它们包装的对象相同的接口,并在将任务委托给包装对象之前或之后添加自己的行为。装饰模式表达了类应该对扩展开放但对修改关闭的设计原则。