Objective-C设计模式解析-装饰

看图识模式

每一个早晨出门前都要穿衣打扮,根据参加的场所选择不一样的服饰。
好比如今有若干衣服:运动鞋、运动裤、卫衣、衬衫、西服、皮鞋、内衣等。
提出需求: 这周分别参加公益酒会、运动会、cosplay三个活动。怎么搭配这些衣服了,设计成类如何实现?编程

能够这样搭配,如图性能

clipboard.png

继承方式

生成3个子类分别继承Person类:学习

  • 第一个子类styleOne,披风+红内衣
  • 第二个子类styleTwo,卫衣+运动鞋+短裤
  • 第三个子类styleThree,西服+皮鞋+领带

看起来很好,并且解决了问题。可是发现一样也有一些问题,ui

  • 这些子类都是静态的、不可改变的,好比天冷了我须要加一件棉服怎么办?每一个子类都要改变
  • 一年四季咱们穿的衣服变幻无穷,这样须要建立多少个子类呢?确定是一个很恐怖的数量

一个子类

在一个子类里定义所有的功能,须要穿哪件衣服(哪一个功能)就穿哪件衣服(调用对应功能),可是这不知足单一职责原则(不一样的功能不该该放在同一个类里面),并且这个类会过于臃肿而没法维护,而且大部分功能是使用不到的,只有在相应的场景才会须要。
并且若是增长新功能也要改变这里类,也不符合开放-封闭原则。atom

分析&解决

经过上面的例子咱们能够发现2个规律spa

  • 第一: 穿的衣服是能够任意组合的,理论上穿几件、穿哪一种类型均可以。设计

    也就是动态添加
  • 第二: 穿完一件衣服能够再穿另外一件衣服,穿完一件衣服后我仍是我(类型没发生变化)。穿一件衣服以前不用关心我穿没穿衣服、穿了几件衣服,穿完以后一样也是。调试

    添加以后类型不发生变化,添加先后均可以被一致对待。也就是装饰前和装饰后
    没有什么不一样

为了实现这些目的,能够这样设计:code

首先,声明一个抽象接口Person,它有一个show方法来展现当前的穿着打扮。具体的人(Person)实现这个接口好比黄种人(YellowMan),show方法只输出人名,在未装饰以前就只是一个单纯的人。
而后再定义一个装饰类Decorator,它也实现接口Person,但不一样的是它拥有一个具体对象(YellowMan)的引用,并且多了一个addBehavior方法,这个方法里实现对具体对象的装饰(添加职责)。
最后建立具体的Decorator类,实现具体的addBehavior方法。
把一个具体的人类(Person)传递建立一个具体的装饰类,因为装饰类(Decorator)和人类(Person)拥有相同的接口,因此它俩的对外使用是一致的。当调用show的时候,经过对具体人类(Person)的引用调用它对应的show方法,同时调用装饰方法(addBehavior),达到了添加职责的目的。对象

看一下设计类图:

clipboard.png

这样咱们就能够把任意的装饰类链接起来使用,用图表示应该是这样的

clipboard.png

有几件衣服(职责),就建立几个装饰类,具体怎么穿就能够随意搭配了。下面看一下代码怎么写?

代码示例

抽象接口Person

@protocol Person <NSObject>

- (void)show;

@end

具体人YellowMan实现这个接口Person

#import "Person.h"

@interface YellowMan : NSObject <Person>

- (instancetype)initWithName:(NSString *)name;
- (void)show;

@end

@interface YellowMan ()

@property (nonatomic, copy) NSString *name;

@end

@implementation YellowMan 

- (instancetype)initWithName:(NSString *)name
{
    self = [super init];
    if (self) {
        _name = name;
    }
    return self;
}

- (void)show
{
    NSLog(@"我是: %@", self.name);
}

@end

定义一个装饰类Decorator

@interface Decorator : NSObject <Person>

- (instancetype)initWithPerson:(id <Person>)person;
- (void)show;

@end

 @interface Decorator ()

@property (nonatomic, strong) id <Person>person;

@end

@implementation Decorator

- (instancetype)initWithPerson:(id <Person>)person
{
    self = [super init];
    if (self) {
        _person = person;
    }
    return self;
}

- (void)show
{
    [self.person show];
}

@end

定义具体的装饰类: 衬衫ShirtDecorator

@interface ShirtDecorator : Decorator

@end

@implementation ShirtDecorator

- (void)show
{
    [super show];
    [self addBehavior];
}

- (void)addBehavior
{
    NSLog(@"-- 穿衬衫");
}

@end

定义具体的装饰类: 西装SuitDecorator

@interface SuitDecorator : Decorator

@end

@implementation SuitDecorator

- (void)show
{
    [super show];
    [self addBehavior];
}

- (void)addBehavior
{
    NSLog(@"-- 穿西装");
}

@end

其它的装饰类都相似,再也不一一写了;

client调用

YellowMan *aMan = [[YellowMan alloc] initWithName:@"小明"];
    ShirtDecorator *shirtA = [[ShirtDecorator alloc] initWithPerson:aMan];
    SuitDecorator *suitA = [[SuitDecorator alloc] initWithPerson:shirtA];
    [suitA show];
    
    // 小李是超人,内衣穿外面
    YellowMan *bMan = [[YellowMan alloc] initWithName:@"小李"];
    ShirtDecorator *shirtB = [[ShirtDecorator alloc] initWithPerson:bMan];
    SuitDecorator *suitB = [[SuitDecorator alloc] initWithPerson:shirtB];
    UnderwearDecorator *underwear = [[UnderwearDecorator alloc] initWithPerson:suitB];
    [underwear show];

运行结果:

clipboard.png

咱们发现被装饰过的对象任然和没装饰前的同样,它的功能没有发生改变,只是多了被装饰的功能,使用方式也没有发生变化。
并且被装饰后的对象还能够被继续装饰,装饰多少次和装饰顺序彻底能够动态控制。

模式定义

定义

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

结构图

clipboard.png

装饰模式包含以下角色:

  • 抽象组件角色(Component):定义一个对象接口,以规范准备接受附加责任的对象,便可以给这些对象动态地添加职责
  • ConcreteComponent: 具体构件,也能够给这个对象添加一些职责
  • Decorator: 抽象装饰类,继承了Component,维持一个指向构件Component对象的实例,并定义一个与抽象组件角色Component接口一致的接口
  • ConcreteDecorator: 具体装饰类,起到给Component添加职责的功能。

经过上面的结构咱们发现:

  • 装饰者和被装饰对象有相同的超类型。
  • 你能够用一个或多个装饰者包装一个对象。
  • 既然装饰者和被装饰对象有相同的超类型,因此在任何须要原始对象(被包装的)的场合 ,能够用装饰过的对象代替它。
  • 装饰者能够在所委托被装饰者的行为以前与/或以后,加上本身的行为,以达到特定的目的。
  • 对象能够在任什么时候候被装饰,因此能够在运行时动态地、不限量地用你喜欢的装饰者来装饰对象。

类应该对扩展开放,对修改关闭

特征

目的

扩展对象的功能

装饰模式能够提供比继承更多的灵活性。装饰模式容许系统动态决定“贴上”一个须要的“装饰”,或者除掉一个不须要的“装饰”。继承关系则不一样,继承关系是静态的,它在系统运行前就决定了。

使用场景

在如下状况下可使用装饰模式:

  • 在不影响其余对象的状况下,以动态、透明的方式给单个对象添加职责。
  • 须要动态地给一个对象增长功能,这些功能也能够动态地被撤销。
  • 当不能采用继承的方式对系统进行扩充或者采用继承不利于系统扩展和维护时。不能采用继承的状况主要有两类:第一类是系统中存在大量独立的扩展,为支持每一种组合将产生大量的子类,使得子类数目呈爆炸性增加;第二类是由于类定义不能继承(如final类,object-c中不会有这种状况)

透明性

装饰模式可分为透明装饰模式和半透明装饰模式:在透明装饰模式中,要求客户端彻底针对抽象编程,装饰模式的透明性要求客户端程序不该该声明具体构件类型和具体装饰类型,而应该所有声明为抽象构件类型;
例如:

// 应该这样
    id <Person>person = [[YellowMan alloc] initWithName:@"小明"];
    id <Person>shirtPerson = [[ShirtDecorator alloc] initWithPerson:person];
    id <Person>suitPerson = [[SuitDecorator alloc] initWithPerson:shirtPerson];
    [suitPerson show];
    
    // 不该该这样
    YellowMan *aMan = [[YellowMan alloc] initWithName:@"小明"];
    ShirtDecorator *shirtA = [[ShirtDecorator alloc] initWithPerson:aMan];
    SuitDecorator *suitA = [[SuitDecorator alloc] initWithPerson:shirtA];
    [suitA show];

半透明装饰模式允 许用户在客户端声明具体装饰者类型的对象,调用在具体装饰者中新增的方法。

然而,纯粹的装饰模式很难找到。装饰模式的用意是在不改变接口的前提下,加强所考虑的类的性能。在加强性能的时候,每每须要创建新的公开的方法。这就致使了大多数的装饰模式的实现都是“半透明”的,而不是彻底透明的。换言之,容许装饰模式改变接口,增长新的方法。这意味着客户端能够声明ConcreteDecorator类型的变量,从而能够调用ConcreteDecorator类中才有的方法。

优缺点

优势

  • 装饰模式与继承关系的目的都是要扩展对象的功能,可是装饰模式能够提供比继承更多的灵活性。
  • 能够经过一种动态的方式来扩展一个对象的功能,经过配置文件能够在运行时选择不一样的装饰器,从而实现不一样的行为。
  • 经过使用不一样的具体装饰类以及这些装饰类的排列组合,能够创造出不少不一样行为的组合。可使用多个具体装饰类来装饰同一对象,获得功能更为强大的对象。
  • 具体构件类与具体装饰类能够独立变化,用户能够根据须要增长新的具体构件类和具体装饰类,在使用时再对其进行组合,原有代码无须改变,符合“开闭原则”

缺点

  • 这种比继承更加灵活机动的特性,也同时意味着装饰模式比继承更加易于出错,排错也很困难,对于屡次装饰的对象,调试时寻找错误可能须要逐级排查,较为烦琐。
  • 产生不少具体装饰类。这些装饰类和它们之间相互链接的方式将增长系统的复杂度,加大学习与理解的难度。

Objective-C中的应用

根据Objective-C的特性,有两种实现方式:

  • 经过真正的子类实现装饰,上面通用的结构
  • 经过分类Category实现装饰

第二种方式是使用了Objective-C的语言功能,经过分类向类添加行为,没必要进行子类化,这并不是标准的装饰模式结构,可是实现了装饰模式一样的需求。尽管使用分类来实现装饰模式跟原始风格有偏离,可是实现少许的装饰器的时候,它比真正子类方式更加轻量、更加容易。

相关文章
相关标签/搜索