iOS实现装饰模式 其实就是类别,需要添加属性的话需要runtime ,也不难。 简单高效。
在面向对象编程中有个重要的原则,里氏代换原则:一个软件实体如果使用的是一个父类的话,那么一定适用其子类,而且它察觉不出父类对象与子类对象的区别。也就是说,在软件设计里面,把父类替换成它的子类,程序的行为没有变化。简单的说,子类类型必须能替换掉它的父类类型。
就好像继承的概念,子类继承自父类,那么子类可以以父类的身份出现。有这样一个问题,在面向对象设计中,一个是鸟类,一个是企鹅类,如果鸟是可以飞得,企鹅不会飞,那么企鹅是鸟么?企鹅可以继承自鸟类么?
需要面向对象设计,那么意味着,子类拥有父类所以非private的属性和行为,鸟会飞,而企鹅不会,所以企鹅是鸟,但它不能继承自上面那个会飞的鸟类,抽象出一个更高的鸟类,然后分为会飞的鸟子类、不会飞的鸟子类,企鹅应该继承自不会飞的鸟子类。
这因为有了里氏代换原则,才使得继承复用成为可能,只有当子类可以替换掉父类,软件单位的功能不受到影响时,父类才能真正被复用,而子类也可以在父类的基础上增加新的行为。正是由于子类的可替换性才使得父类类型模块在无需修改的情况下就能扩展,这是前面提到的,对扩展的开放,对修改的封闭(ocp原则)。
装饰模式
穿衣问题,要求写一个给人搭配不同服饰的系统,那种可以换各种各样衣服和裤子的服饰系统,如下图:
首先排除这样的结果设计,如果我需要新增加超人的服饰设计,又得更改Person类,很明显违背了开发-封闭原则(ocp,对扩展的开放,对修改的封闭)。其实把这些服饰类写成子类就好,代码结构:
如此,需要增加超人的装扮 ,只需要增加子类即可。不需要对已有的代码进行修改。
就可以用到装饰模式:动态的给一个对象添加一些额外的职能,就增加功能来说,装饰模式比添加子类更加灵活。无论是衣服、鞋子、裤子等,其实我们都可以把它理解为对Person的装饰,那么有下图结构:
代码:
Person类:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
#import <Foundation/Foundation.h>
@interface
ZYPerson :
NSObject
{
@protected
NSString
*_name;
}
- (instancetype)initWithName:(
NSString
*)name;
- (
void
)display;
@end
#import "ZYPerson.h"
@implementation
ZYPerson
- (instancetype)initWithName:(
NSString
*)name
{
if
(
self
= [
super
init]) {
_name = name;
}
return
self
;
}
- (
void
)display
{
NSLog
(@
"装扮的%@:"
,_name);
}
@end
|
clothing类:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
#import "ZYPerson.h"
@interface
ZYClothing : ZYPerson
@property
(
nonatomic
, strong) ZYPerson *decorate;
- (instancetype)initWithDecorate:(ZYPerson *)decorate;
@end
#import "ZYClothing.h"
@implementation
ZYClothing
- (instancetype)initWithDecorate:(ZYPerson *)decorate
{
if
(
self
= [
super
init]) {
_decorate = decorate;
}
return
self
;
}
- (
void
)display
{
if
(
self
.decorate) {
[
self
.decorate display];
}
}
@end
|
TShirts类:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
#import "ZYClothing.h"
@interface
ZYTShirts : ZYClothing
@end
#import "ZYTShirts.h"
@implementation
ZYTShirts
- (
void
)display
{
[
super
display];
NSLog
(@
"大衬衫"
);
}
@end
|
Pants类:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
#import "ZYClothing.h"
@interface
ZYPants : ZYClothing
@end
#import "ZYPants.h"
@implementation
ZYPants
- (
void
)display
{
[
super
display];
NSLog
(@
"大裤衩"
);
}
@end
|
Shoe类:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
#import "ZYClothing.h"
@interface
ZYShoe : ZYClothing
@end
#import "ZYShoe.h"
@implementation
ZYShoe
- (
void
)display
{
[
super
display];
NSLog
(@
"破鞋子"
);
}
@end
|
viewController里面的代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
|
#import "ViewController.h"
#import "ZYPerson.h"
#import "ZYClothing.h"
#import "ZYTShirts.h"
#import "ZYPants.h"
#import "ZYShoe.h"
@interface
ViewController ()
@end
@implementation
ViewController
- (
void
)viewDidLoad {
[
super
viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
ZYPerson *person = [[ZYPerson alloc] initWithName:@
"晶童鞋"
];
ZYClothing *clothing = [[ZYClothing alloc] initWithDecorate:person];
ZYTShirts *shirts = [[ZYTShirts alloc] init];
ZYPants *pants = [[ZYPants alloc] init];
ZYShoe *shoe = [[ZYShoe alloc] init];
//装扮过程,相当于在室内穿衣服,控制器并不知道它是怎么的顺序
shirts.decorate = clothing;
pants.decorate = shirts;
shoe.decorate = pants;
[shoe display];
//第二次装扮
pants.decorate = clothing;
shoe.decorate = pants;
shirts.decorate = shoe;
[shirts display];
}
@end
|
运行效果图:
额, 这个例子是模仿<大话设计模式>这本书上的例子, 但是感觉不太好. 在iOS中的装饰模式实现有2种方法,
- 系统提供的类别, 某个类的主功能已经完成, 我们需要扩充一些方法, 使用类别更简单的完成此目标;
- 第二种方式, 比如Person类功能已经很完善,会开车,工作,学习 ,..., 在极个别场景下需要新的功能, 比如穿衣服的功能, (如果是全量场景都需要此功能还是修改Person合适)
就使用一个新类CloseingPerson(注意:CloseingPerson继承NSObject,不是继承自Person), 新类持有Person, CloseingPerson中添加衣服属性和穿衣服的动作, 在这个特殊场景下使用CloseingPerson, 同时如果需要使用Person的工作,学习方法, 可以通过[self.person work]进行处理.
对使用者而言,有以下好处,- 还是同一个Person, 拥有Person的所有属性和方法,
- 同时有可以新增穿衣服的功能,
- 对于已经完善的Person功能无任何修改, 不影响Person的核心功能
装饰模式总结:
我觉得装饰模式,是为已有功能动态的添加更多功能的一种方法。但是到底什么时候用它呢?
在本文的最初设计中,当系统需要添加新功能的时候,是向旧的类中添加新的代码,这些新增的代码通常装饰了原有类的核心职能或主要行为。这种设计方式问题在于,他们在主类中增加了新的字段、新的方法、新的逻辑,从而增加了主类的复杂度。而这些新加入的东西仅仅是为了满足一些在某种特定情况下才会执行的特殊行为的需求。
而装饰模式提供了一个非常好的解决方案,它把每个要装饰的功能放在单独的类中,并让这个类包含它所要装饰的对象,因此,当执行特殊行为时,在viewController里就可以根据需求有选择、按顺序的使用装饰功能包装对象了。
装饰模式的优点:
- 把类中的装饰功能从类中搬移出去,这样可以简化原有的类。
- 当有效的把类中的核心功能和装饰功能区分开了,可以去除相关类中重复的装饰逻辑。