桥接模式

要实现多个有相同的类的操作,他们又有各自的控制单元,如车控钥匙控制车辆的上锁、解锁。如果每种钥匙都对应每种车辆的话,具体代码会剧增。我们可以将车辆型号和钥匙隔控制逻辑离开来。钥匙的同一操控可以复用和扩展,而不会影响其他电视剧型号。桥接的目标是把抽象层次结构从实现中分离出来,让它能够独立变更。

桥接模式类图:
avatar

图中我们可以通过UniversalApp来实现对MoBike和OFOBike的控制操作,他们通过各自的抽象类进行桥接在一起,实现控制与实现的分离。

UniversalApp模型的实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
- (void)lock
{
[self sendCommand:Command_lock];
}
- (void)open
{
[self sendCommand:Command_open];
}
- (void)broken
{
[self sendCommand:Command_broken];
}
- (void)reserve
{
[self sendCommand:Command_reserve];
}

App模型的实现

1
2
3
4
- (void)sendCommand:(Command)command
{
[self.bike receiveCommand:command];
}

本节工程示例

适配器模式

在一些应用中,我们创建的类已经拥有的一定的行为,而我们客户端需要拥有自己定义的行为,我们就可以使用适配器模式,将原有的行为传递给使用者。例如像我们大陆去香港购物,香港当地是用港币结算,但在大陆习惯用人民币来衡量物品的价值,这时候的思维往往会用港币转换成人民币来计算,这时我们就可以用适配器模式来适配港币的结算来获得人民币的价值。实现适配器模式有两种方式,已是通过继承适配者,调用父类方法来适配,也称为类适配。另一种是通过组合方式,持有适配对象,来实现适配功能。

类适配器模式类图
avatar

适配功能在Adapter_Class的实现中进行,调用父类的方法,进行操作。

Adapter_Class的实现

1
2
3
4
5
6
7
8
9
10
const float rate = 0.82;

@implementation Adapter_Class

- (float)getSellPriceUseCNY
{
return [super getSellPriceUseHKD] * rate;
}

@end

对象适配器模式类图
avatar
Adapter_Object的实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
const float rate_o = 0.82;

@implementation Adapter_Object

- (instancetype)init
{
if (self = [super init]) {
_goods = [[HKGoods alloc] init];
}
return self;
}

- (float)getSellPriceUseCNY
{
return [self.goods getSellPriceUseHKD] * rate_o;
}

@end

本节工程示例

外观模式

在一些子系统逐渐复杂的应用场景中,我们可以将一组不同的接口提供统一接口。定义上层接口,通过降低复杂度和隐藏子系统间的通信及依存关系,让子系统更易于使用。例如在使用XMPP协议的登录过程,我们在登录的时候分好几个步骤,同时UI也会跟随着登录状态进行相应的变化,我们可以统一成一个登录接口,供客户端调用。使用这种方式让子系统更容易使用的方式就是外观模式。

外观模式类图:

avantar

客户端要实现登录功能,只需调用LoginManager的loginAction方法即可完成UI变化和XMPP登录的一整套流程,简化了登录的调用。
其中登录接口的实现

1
2
3
4
5
6
7
8
9
10
11
12
- (void)loginAction
{

XMPPLogin *login = [[XMPPLogin alloc] init];
UIStatue *ui = [[UIStatue alloc] init];

[ui logining];
[login connetXMMPServer];
[login authenticationPassWord];
[login xmppDidAuthenticate];
[ui logined];
}

本节工程示例

组合模式

把一些相同基类型的对象组合到树状结构中,父节点包含同类型的子节点,像平时公司的组织架构、家族结构、公司同一系列产品都可以使用这种模式。拿公司组织架构来说,公司包含多个部门,部门又包含多个员工。我们可以通过统一的接口把这整个复杂结构作为一个整体来使用,如通知公司放假、公司传达到各个部门,各个部门再通知下面的员工。其实也就是将对象组合成“部分-整体”的关系,让其中的每个节点具有相同的抽象接口。

组合模式类图:

avantar

MemberModel定义了统一的接口,它的操作可以通过递归传达到借点的每一处。

其中Company的实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
- (void)addMember:(id <MemberModel>)member
{
[self.childMembers addObject:member];
}
- (void)removeMember:(id <MemberModel>)member
{
[self.childMembers removeObject:member];
}

- (instancetype)init
{
if (self = [super init]) {
_childMembers = [NSMutableArray<MemberModel> array];
}
return self;
}

本节工程示例

装饰模式

有些时候我们需要对现有的行为进行一定的修饰,又希望不改变原有的功能,我们就可以使用装饰模式,也是动态的给一个对象添加一些扩展功能。例如就跑步来说,最开始只有跑步的功能,现在在不需要改变原有的功能上,给它加点快跑和慢跑的修饰。我们抽象一个父类,细化另一个抽象类,这个抽象类包含了父类中的一个引用,具体的修饰行为交给他的子类来表示。

装饰模式类图:

avantar

MemberModel定义了统一的接口,它的操作可以通过递归传达到借点的每一处。

其中RunDecorator的实现

1
2
3
4
5
6
7
8
9
10
11
12
13
- (instancetype)initWithRun:(id<RunProtocol>)runer
{
self = [super init];
if (self) {
_runer = runer;
}
return self;
}

- (NSString *)run
{
return nil;
}

除了以上的这种装饰方式外,在OC中还可以使用类别来进行装饰,它装饰饰的方式是编译时绑定的,而且它成为了类的一部分。但在一些简单装饰的话,可以用类别来实现,使用起来更为简洁方便。

本节工程示例

享元模式

在应用程序要使用很多对象,我们可以共享这些对象的一些共同点,以节省内存空间。就像奥运会一样,每一场比赛都需要比赛场地,我们不可能每场比赛都使用新的场馆,现实没那么多的场地资源支撑,同一类型的比赛使用同一场地,这样可以节省很多资源。就像我们需要某种资源了,看看是否已经拥有,有的话就直接使用,没有再创建,也就是共享可用对象。

享元模式类图:

avantar

PlaygroudFactory定义工厂方法,它利用缓存池来保证对象的共享使用。

其中PlaygroudFactory的实现部分

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
- (id<Playground>)playgroudWithStation:(PlaygroudStation)station
{
if (self.playgroudPools == nil) {
self.playgroudPools = [[NSMutableDictionary alloc] initWithCapacity:kTotalNumberPlaygroud];
}

BJPlaygroud *playgroud = [self.playgroudPools objectForKey:[NSNumber numberWithInteger:station]];

if (playgroud == nil) {

playgroud = [[BJPlaygroud alloc] init];

switch (station) {
case kPlaygroud_NationalStadium:
playgroud.name = @"国家体育场";
playgroud.events = @"田径、足球";
break;

case kPlaygroud_NationalAquaticsCenter:
playgroud.name = @"国家游泳中心";
playgroud.events = @"游泳";
break;

case kPlaygroud_NationalIndoorStadium:
playgroud.name = @"国家体育馆";
playgroud.events = @"体操、蹦床、手球";
break;
case kPlaygroud_ShootingRangeHall:
playgroud.name = @"北京射击馆";
playgroud.events = @"射击";
break;

default:
break;
}
[self.playgroudPools setObject:playgroud forKey:[NSNumber numberWithInt:station]];
}

return playgroud;
}

本节工程示例

代理模式

为其他对象提供一种代理以控制对这个对象的访问。例如我要购买火车票,如果需要跑到车站去买的话,会花很多时间,这个时候我就可以找个附近的代售点进行购买。除了像这种情况,还可以创建重型对象,来延迟加载和调用,根据不同的访问权限控制对原对象的访问也可以使用这种方式。

代理模式类图:

avantar

客户端要进行买票操作的时候,通过代理对象调用上层抽象接口,来实现实际售票对象的卖票功能,其中CommissionMerchant的实现部分

1
2
3
4
5
6
7
8
9
10
11
12
13
- (instancetype)init
{
self = [super init];
if (self) {
_station = [[Station alloc] init];
}
return self;
}

- (void)sellTicket
{
[self.station sellTicket];
}

本节工程示例