装饰模式

2018-06-04  本文已影响0人  iOS扫地僧

Objective-C编程之道 iOS设计模式解析
iOS设计模式解析-工厂模式
iOS设计模式解析-抽象工厂模式
iOS设计模式解析-外观模式
iOS设计模式解析-中介者模式
iOS设计模式解析-观察者模式
iOS设计模式解析-装饰模式
iOS设计模式解析-责任链模式
iOS设计模式解析-模板方法
iOS设计模式解析-策略模式
iOS设计模式解析-享元模式
iOS设计模式解析-代码地址

装饰模式:动态地给一个对象添加一些额外的职责。就扩展功能来说,装饰模式相比生成子类更为灵活。

何时使用装饰模式

在以下情形,自然会考虑使用这一模式。

改变对象的“外表”和“内容”

“外表”变更(装饰模式)

“内容”变更(策略模式)

下面将为UIImage创建图像滤镜来说明装饰模式

装饰模式是向对象添加新的行为与职责的一种方式,它不改变任何现有行为与接口。比如说有个图像对象,它只包含让客户端管理其属性的接口,仅此而已。我们想向它添加点花哨的东西,比如变换滤镜,但不想修改图像对象的现有接口。我们能做的是,再定义一个跟这个图像对象一样的类,但它包含对另一个图像对象的引用,增加这个图像对象的行为。新的类有一个用绘图上下文绘制自身的方法。在这个绘图方法中,它向内嵌的图像引用应用变换算法,绘制整个图像,然后返回结果图像。可以把这个过程想象为在图片上放一层玻璃。图片不用去管玻璃,而且我们看它的时候,还管它叫图片。玻璃本身可以有颜色,在表面有波纹,或者其他别的可以让图片看上去不同的东西。以后如果想向图像应用另一层滤镜,可以再定义一个滤镜类,就像变换用的滤镜那样,通过这个类,可以应用同样的机制来向图像增加自己的操作。变换滤镜之外的其他滤镜,可以获取结果图像,并继续处理。但有一点,在装饰滤镜的流水线上传递的图像,不必是原来的那个,但必须是同样的类型。因此从变换滤镜返回的图像是一幅变换后的图像。然后当它被传给颜色滤镜的时候,返回的图像就是一幅经过着色的变换后的图像。

image.png
在这里,具体组件是一个UIImage类型,但我们不想只是为了让它参与进来就继承它,因此我们为它定义了一个分类。UIImage( ImageComponent)分类采用了ImageComponent协议。因为协议中定义的方法在UIImage中已经都有了,所以不必在分类中进行实现。
ImageTransformFilterImageShadowFilter专注于通过重载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原有行为的完整性。

上一篇下一篇

猜你喜欢

热点阅读