美文网首页
装饰模式

装饰模式

作者: iOS扫地僧 | 来源:发表于2018-06-04 14:03 被阅读0次

    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原有行为的完整性。

    相关文章

      网友评论

          本文标题:装饰模式

          本文链接:https://www.haomeiwen.com/subject/xgaesftx.html