设计模式-状态模式

定义:允许一个对象在其内部状态改变时改变它的行为,对象看起来似乎修改了它的类。其别名为状态对象(Objects for States),状态模式是一种对象行为型模式。

实例:状态模式用于解决复杂对象的状态转换以及不同状态下行为的封装问题。当系统中某个对象存在多个状态,这些状态之间可以进行转换,所以对象在不同状态下具有不同行为时可以使用状态模式。状态模式将一个对象的状态从该对象中分离出来,封装到专门的状态类中,使得对象状态可以灵活变化。例如:根据存钱余额来自动设置账户的状态,银行账户在不同状态下,进行存钱、取钱和借钱的行为。在不同状态下,这些行为得到的回复也不一样,比如说没有余额时无法取钱,只能借钱。

角色:

  • Context: 环境类
  • State: 抽象状态类
  • ConcreteState: 具体状态类

对应类图:

实例:

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
@interface State : NSObject

- (BOOL)saveMoney:(CGFloat)money;
- (BOOL)drawMoney:(CGFloat)money;
- (BOOL)borrowMoney:(CGFloat)money;

@end

@implementation Satate

- (BOOL)saveMoney:(CGFloat)money
{
return NO;
}
- (BOOL)drawMoney:(CGFloat)money
{
return NO;
}
- (BOOL)borrowMoney:(CGFloat)money
{
return NO;
}

@end

Concreate State

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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
// 存款富余状态
@interface RichState : State
@end

@implementation RichState
// 存钱
- (BOOL)saveMoney:(CGFloat)money
{
NSLog(@"欢迎存钱 %.2f", money);
return YES;
}
// 取钱
- (BOOL)drawMoney:(CGFloat)money
{
NSLog(@"欢迎取钱 %.2f", money);
return YES;
}
// 借钱
- (BOOL)borrowMoney:(CGFloat)money
{
NSLog(@"您还有余额,请先花完余额");
return NO;
}

@end

// 零存款零负债状态
@interface ZeroState : State
@end

@implementation ZeroState
// 存钱
- (BOOL)saveMoney:(CGFloat)money
{
NSLog(@"欢迎存钱 %.2f", money);
return YES;
}
// 取钱
- (BOOL)drawMoney:(CGFloat)money
{
NSLog(@"您当前没有余额");
return NO;
}
// 借钱
- (BOOL)borrowMoney:(CGFloat)money
{
NSLog(@"欢迎借钱 %.2f", money);
return YES;
}

@end

// 负债状态
@interface DebtState : State
@end

@implementation DebtState
// 存钱
- (BOOL)saveMoney:(CGFloat)money
{
NSLog(@"欢迎还钱 %.2f", money);
return YES;
}
// 取钱
- (BOOL)drawMoney:(CGFloat)money
{
NSLog(@"您当前没有余额");
return NO;
}
// 借钱
- (BOOL)borrowMoney:(CGFloat)money
{
NSLog(@"上次欠的账还没有还清,暂时无法借钱");
return NO;
}

@end

账户类

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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
// 银行账户类
@interface Account : NSObject
// 存钱
- (void)saveMoney:(float)money;
// 取钱
- (void)drawMoney:(float)money;
// 借钱
- (void)borrowMoney:(float)money;

@end

@interface Account ()

@property (nonatomic, assign) CGFloat money; // 余额
@property (nonatomic, strong) State *state; // 账户状态

@end

@implementation Account

// 初始化账户
- (instancetype)init
{
self = [super init];
if (self)
{
_money = 0;
_state = [ZeroState new];
}
return self;
}

// 存钱
- (void)saveMoney:(CGFloat)money
{
if ([_state saveMoney:money])
{
_money += money;
[self updateState];
}
NSLog(@"余额:%.2f", _money);
}
// 取钱
- (void)drawMoney:(CGFloat)money
{
if ([_state drawMoney:money])
{
_money -= money;
[self updateState];
}
NSLog(@"余额:%.2f", _money);
}
// 借钱
- (void)borrowMoney:(CGFloat)money
{
if ([_state borrowMoney:money])
{
_money -= money;
[self updateState];
}
NSLog(@"余额:%.2f", _money);
}

// 更新账户状态
- (void)updateState
{
if (_money > 0)
{
_state = [RichState new];
}
else if (_money == 0)
{
_state = [ZeroState new];
}
else
{
_state = [DebtState new];
}
}

@end

使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 初始化银行账户
Account *bankAccount = [Account new];
// 取 50
[bankAccount drawMoney:50]; // 余额:0 您当前没有余额
// 存 100
[bankAccount saveMoney:100]; // 余额:0 欢迎存钱 100.00
// 借 100
[bankAccount borrowMoney:100]; // 余额:100 您还有余额,请先花完余额
// 取 100
[bankAccount drawMoney:100]; // 余额:100 欢迎取钱 100.00
// 借 100
[bankAccount borrowMoney:100]; // 余额:0 欢迎借钱 100.00
// 借 50
[bankAccount borrowMoney:50]; // 余额:-100 上次欠的账还没有还清,暂时无法借钱

优点:

  • 封装了状态的转换规则,在状态模式中可以将状态的转换代码封装在环境类或者具体状态类中,可以对状态转换代码进行集中管理,而不是分散在一个个业务方法中。
  • 将所有与某个状态有关的行为放到一个类中,只需要注入一个不同的状态对象即可使环境对象拥有不同的行为。
  • 允许状态转换逻辑与状态对象合成一体,而不是提供一个巨大的条件语句块,状态模式可以让我们避免使用庞大的条件语句来将业务方法和状态转换代码交织在一起。
  • 可以让多个环境对象共享一个状态对象,从而减少系统中对象的个数。

缺点:

  • 状态模式的使用必然会增加系统中类和对象的个数,导致系统运行开销增大。
  • 因为新增或者删除一个状态需要修改相关的状态类,所以不符合开闭原则。