iOS设计模式-装饰器模式

1. 什么是装饰器设计模式

装饰模式(Decorator Pattern 定义

Attach additionalresponsibilities to an object dynamically keeping the same interface.Decorators provide aflexible alternative to subclassing for extending functionality.(动态地给一个对象添加一些额外的职责。就增加功能来说,装饰模式相比生成子类更为灵活。)

是指在不改变现有对象结构的情况下,动态地给该对象增加一些职责(即增加其额外功能)的模式

2. 角色组成

装饰模式主要包含以下角色:

  • 抽象构件(Component):定义一个抽象接口以规范准备接收附加责任的对象。
  • 具体构件(ConcreteComponent):实现抽象构件,通过装饰角色为其添加一些职责。
  • 抽象装饰(Decorator):继承抽象构件,并包含具体构件的实例,可以通过其子类扩展具体构件的功能。
  • 具体装饰(ConcreteDecorator):实现抽象装饰的相关方法,并给具体构件对象添加附加的责任。

UML关系图:


装饰器模式

3. 完全透明与半透明模式

  • 完全透明模式:装饰模式对客户端的透明性要求程序不要声明给一个ConcreteComponent类型的变量,而应当声明一个Component类型的变量。也就是说客户端在调用时,只能与Componnet实例来调用代码,这就要求ConcreteDecorator的接口与Component保持一致,不去新增接口,只对原有的接口进行增强。
  • 半透明模式:完全透明模式是比较少见的,因为在增强功能的过程中,往往会新增接口。这就导致客户端调用新接口的时候,必须声明ConcreteDecorator类型的变量,这样才能调用新增的接口。这种有新增功能的情况称为半透明模式,也称作半装饰、半适配器模式。

4. 代码示例

我们一英雄联盟英雄技能为例。英雄联盟作为一款竞技网游,每个英雄(Hero)本身具有一个被动技能buff,并且可以通过击杀特定的中立野怪而获取到对应的buff在一定时间内增加自身的属性。为了实现被加持不同buff的英雄,有哪些方式呢?

  1. 不改变抽象类的情况下,最容易想到的方式,自然是采取继承的方式。但是如果为每个英雄加持不同buff都新建一个类的话,这样无疑会照成类爆炸性增长,毕竟英雄数现在就有上百个了,未来还一直在增加。
  2. 使用装饰器模式为其动态的新增或者移除buff。
    无疑在这种场景下,选择装饰器模式去实现是更加合理的。接下来对其进行代码实现。

先画出UML类图:


先定义抽象构件类Hero,通过方法blessBuff展示英雄此时所加持的buff

// Hero.h
#import 

@interface Hero : NSObject
- (void)blessBuff;
@end

// Hero.m
#import "Hero.h"

@implementation Hero

- (void)blessBuff {
    NSAssert(false, @"must implement in subClass");
}

@end

定义英雄盖伦Galen和Timo

// Galen.h
#import "Hero.h"

@interface Galen : Hero

@end

// Galen.m
#import "Galen.h"

@implementation Galen
- (void)blessBuff {
    NSLog(@"盖伦被动技能:脱离战斗后回血加快");
}
@end

// Timo.h
#import "Hero.h"

@interface Timo : Hero

@end

// Timo.m
#import "Timo.h"

@implementation Timo
- (void)blessBuff {
    NSLog(@"提莫被动技能:脱离战斗后,静止不动一段时间进入隐身");
}
@end

定义buff抽象装饰器BuffDecorator

#import "Hero.h"

// BuffDecorator.h
@interface BuffDecorator : Hero
- (instancetype)initWithHero:(Hero *)hero;
@end

//BuffDecorator.m
#import "BuffDecorator.h"

@interface BuffDecorator()
@property (nonatomic, strong) Hero *hero;
@end

@implementation BuffDecorator

- (instancetype)initWithHero:(Hero *)hero {
    self = [super init];
    if (self) {
        _hero = hero;
    }
    return self;
}

- (void)blessBuff {
    [_hero blessBuff];
    NSLog(@"额外buff:");
}

@end

先来个红buff装饰器

// RedBuffDecorator.h
#import "BuffDecorator.h"

@interface RedBuffDecorator : BuffDecorator

@end

// RedBuffDecorator.m
#import "RedBuffDecorator.h"

@implementation RedBuffDecorator

- (void)blessBuff {
    [super blessBuff];
    NSLog(@"红buff: 攻击附加真实伤害,并造成灼烧效果");
}
@end

再来个蓝buff装饰器

// BlueBuffDecorator.h
#import "BuffDecorator.h"

@interface BlueBuffDecorator : BuffDecorator

@end

// BlueBuffDecorator.m
#import "BlueBuffDecorator.h"

@implementation BlueBuffDecorator
- (void)blessBuff {
    [super blessBuff];
    NSLog(@"蓝buff: 蓝量回复速度加快,并且缩减技能CD");
}
@end

开始调用,全透明模式下,直接声明变量类型为抽象构件类,Hero即可,然后依次使用不同装饰器为其增加不同buff

+ (void)test {
    NSLog(@"----------------Galen----------------------");
    Hero *galen = [Galen new];
    galen = [[RedBuffDecorator alloc] initWithHero:galen];
    [galen blessBuff];
    galen = [[BlueBuffDecorator alloc] initWithHero:galen];
    [galen blessBuff];
    NSLog(@"----------------Timo----------------------");
    Hero *timo = [Timo new];
    timo = [[RedBuffDecorator alloc] initWithHero:timo];
    [timo blessBuff];
    timo = [[BlueBuffDecorator alloc] initWithHero:timo];
    [timo blessBuff];
}

运行结果


5. 分析

  • 采用装饰器模式可以动态的扩展一个实现类的功能
  • 采用装饰模式扩展对象的功能比采用继承方式更加灵活。
  • 可以设计出多个不同的具体装饰类,创造出多个不同行为的组合。
  • 装饰模式增加了许多子类,如果过度使用会使程序变得很复杂

6. 适用场景

  • 可以作为替代继承的一种方式
  • 动态地为对象拓展或撤销功能,但是又不希望对整个类进行拓展或撤销相应功能的情况
  • 为诸多兄弟类拓展功能时

7. 模式拓展

装饰器模式在使用的时候并不一定所有的角色都要有,一些特定情况可以省略一些角色。

  1. 当只有一个具体构件(ConcreteComponent)时,可以省略抽象构件(Component),UML类图如下:


  2. 当只有一个具体装饰类(ConcreteDecorator)时,可以把抽象装饰类和具体抽象类进行合并.UML类图如下:


8. 装饰器模式和代理模式的区别

装饰器模式可以看做是代理模式的一个特殊应用,他们有的代码实现看起来非常像,尤其是在装饰器模式省略了抽象装饰器后。我们来分析下他们的异同点:

相同点

  • 对装饰器模式来说,装饰者和被装饰者都实现同一个 接口。对代理模式来说,代理类和真实处理的类也都实现同一个接口。他们之间的边界确实比较模糊,两者都是对类的方法进行扩展

异同点

  • 代理模式强调对访问的限制,被代理对象往往不易直接获取,或者希望得到隐藏。装饰器模式更多的强调增强功能上,在原有的类上动态地拓展功能
  • 代理模式是把当前的行为或功能委托给其他对象执行,代理类负责接口限定:是否可以调用真实角色,以及是否对发送到真实角色的消息进行变形处理,它不对被主题角色(也就是被代理类)的功能做任何处理,保证原汁原味的调用
  • 装饰模式是在要保证接口不变的情况下加强类的功能,它保证的是被修饰的对象功能比原始对象丰富(当然,也可以减弱),但不做准入条件判断和准入参数过滤,如是否可以执行类的功能,过滤输入参数是否合规等

你可能感兴趣的:(iOS设计模式-装饰器模式)