每一个早晨出门前都要穿衣打扮,根据参加的场所选择不一样的服饰。
好比如今有若干衣服:运动鞋、运动裤、卫衣、衬衫、西服、皮鞋、内衣等。
提出需求: 这周分别参加公益酒会、运动会、cosplay三个活动。怎么搭配这些衣服了,设计成类如何实现?编程
能够这样搭配,如图性能
生成3个子类分别继承Person类:学习
看起来很好,并且解决了问题。可是发现一样也有一些问题,ui
在一个子类里定义所有的功能,须要穿哪件衣服(哪一个功能)就穿哪件衣服(调用对应功能),可是这不知足单一职责
原则(不一样的功能不该该放在同一个类里面),并且这个类会过于臃肿而没法维护,而且大部分功能是使用不到的,只有在相应的场景才会须要。
并且若是增长新功能也要改变这里类,也不符合开放-封闭
原则。atom
经过上面的例子咱们能够发现2个规律spa
第一: 穿的衣服是能够任意组合的,理论上穿几件、穿哪一种类型均可以。设计
也就是动态添加
第二: 穿完一件衣服能够再穿另外一件衣服,穿完一件衣服后我仍是我(类型没发生变化)。穿一件衣服以前不用关心我穿没穿衣服、穿了几件衣服,穿完以后一样也是。调试
添加以后类型不发生变化,添加先后均可以被一致对待。也就是装饰前和装饰后 没有什么不一样
为了实现这些目的,能够这样设计:code
首先,声明一个抽象接口Person,它有一个show方法来展现当前的穿着打扮。具体的人(Person)实现这个接口好比黄种人(YellowMan),show方法只输出人名,在未装饰以前就只是一个单纯的人。
而后再定义一个装饰类Decorator,它也实现接口Person,但不一样的是它拥有一个具体对象(YellowMan)的引用,并且多了一个addBehavior方法,这个方法里实现对具体对象的装饰(添加职责)。
最后建立具体的Decorator类,实现具体的addBehavior方法。
把一个具体的人类(Person)传递建立一个具体的装饰类,因为装饰类(Decorator)和人类(Person)拥有相同的接口,因此它俩的对外使用是一致的。当调用show的时候,经过对具体人类(Person)的引用调用它对应的show方法,同时调用装饰方法(addBehavior),达到了添加职责的目的。对象
看一下设计类图:
这样咱们就能够把任意的装饰类链接起来使用,用图表示应该是这样的
有几件衣服(职责),就建立几个装饰类,具体怎么穿就能够随意搭配了。下面看一下代码怎么写?
抽象接口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];
运行结果:
咱们发现被装饰过的对象任然和没装饰前的同样,它的功能没有发生改变,只是多了被装饰的功能,使用方式也没有发生变化。
并且被装饰后的对象还能够被继续装饰,装饰多少次和装饰顺序彻底能够动态控制。
装饰模式: 动态地给一个对象添加一些额外的职责。就拓展功能来讲,装饰模式相比生成子类更为灵活。
装饰模式包含以下角色:
经过上面的结构咱们发现:
类应该对扩展开放,对修改关闭
装饰模式能够提供比继承更多的灵活性。装饰模式容许系统动态决定“贴上”一个须要的“装饰”,或者除掉一个不须要的“装饰”。继承关系则不一样,继承关系是静态的,它在系统运行前就决定了。
在如下状况下可使用装饰模式:
装饰模式可分为透明装饰模式和半透明装饰模式:在透明装饰模式中,要求客户端彻底针对抽象编程,装饰模式的透明性要求客户端程序不该该声明具体构件类型和具体装饰类型,而应该所有声明为抽象构件类型;
例如:
// 应该这样 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的语言功能,经过分类向类添加行为,没必要进行子类化,这并不是标准的装饰模式结构,可是实现了装饰模式一样的需求。尽管使用分类来实现装饰模式跟原始风格有偏离,可是实现少许的装饰器的时候,它比真正子类方式更加轻量、更加容易。