组合模式,Composite Pattern,是一个非常巧妙的模式。几乎所有的面向对象系统都应用到了组合模式。
通过本文将让你学会软件开发中的"何为树形结构"、"何为组合模式"、"组合模式可以解决的问题"等相关知识。
内容大纲:
1、树形结构
2、组合模式
3、编写文件夹系统
1、树形结构(大神或者计算机基础很好的可以跳过,不过这后面通过用面向对象的代码实现的树形结构,值得一阅)
在介绍组合设计模式之前,有必要先简单讲讲树形结构,百度一下"树形结构",你很容易找到关于树形结构的相关基本概
念:
树形结构是一层次的嵌套结构。 一个树形结构的外层和内层有相似的结构, 所以这种结构多可以递归的表示。
经典数据结构中的各种树状图是一种典型的树形结构:一颗树可以简单的表示为根, 左子树, 右子树。 左子树和右子树又有自己的子树。
树形结构很容易懂,就和倒过来的大树一样,然后有一个根节点,通过根节点我们可以拿到根节点相关联的子节点,通过子节点我们可以拿到子子节点:
回到上面从百度拷贝来的关于"树形结构"的基本概念,它提到树形结构是有层次嵌套的结构,看下面的图,正因为树形结构是层次嵌套的结构,
所以从整体和部分的角度来看,外层和内层具有相似的结构,在算法数据结构里是可以用递归算法表示的,但是要注意,
递归是面向过程语言的说法哦,因为递归思想本质也是站在代码逻辑的执行过程来考虑的。
另外顺便也补充一个数学图形理论的东西:分形。这个也是分形图形理论的内容。
本篇文章参照Objective-C编程之道,iOS设计模式解析一书(Carlo Chung)而来的,参考了其大量的实例与思想,通过写博客的方式,让自己对这些iOS中常见的设计模式做一个总结,以加深自己对设计模式的理解与应用。希望能够对大家有点帮助。
何为组合模式?
组合模式让我们可以把相同基类型的对象组合到树状结构中,其中父节点包含同类型的子节点。换句话说,这种树状结构形成"部分——整体"的层次结构。什么是“部分——整体”的层次结构呢?它是既包含对象的组合又包含叶节点的单个对象的一种层次结构。每个组合体包含的其他节点,可以是叶节点或者其他组合体。这种关系在这个层次结构中递归重复。因为每个组合或叶节点有相同的基类型,同样的操作可应用于它们中的每一个,而不必在客户端作类型检查。客户端对组合与叶节点进行操作时可忽略它们之间的差别。
组合模式:将对象组合成树形结构以表示"部分——整体"的层次结构。组合使得用户对单个对象和组合对象的使用的具有一致性。
何时使用组合模式?
@:想获得对象抽象的树形表示(部分——整体层次结构);
@:想让客户端统一处理组合结构中的所有对象。
在Cocoa Touch框架中使用组合模式
在Cocoa Touch框架中,UIView被组织成一个组合结构。每个UIView的实例可以包含UIView的其他实例,形成统一的树形结构。让客户端对单个UIView对象和UIView的组合统一对待。
窗口中的UIView在内部形成它的子视图。它们的每一个可以包含其他视图而变成自己的子视图的超视图。添加进来的其他UIView成为它的子视图。它们的每一个可以包含其他视图而变成自己的子视图的超视图。UIView对象只能有一个超视图,可以有零到多个子视图。
视图组合结构参与绘图事件处理。当请求超视图为显示进行渲染时,消息会先在超视图被处理,然后传给其子视图。消息会传播到遍及整个树的其他子视图。因为它们是相同的类型——UIView,它们可以被统一处理。
组合模式的实例引用
先看下组合模式的静态结构如图:
基接口是定义了CompanyLeaf类和CompanyComponent类的共同操作的CompanyProtocol。有些操作支队Component有意义,比如addCompany、removeCompany。为什不不把这些方法放在CompanyComponent类中呢?因为我们不想让客户端在运行时知道它们在处理哪种类型的节点,也不想把组合结构的内部细节暴漏给客户端。这就是为什么虽然操作只对CompanyComponent有意义,我们还是把它们声明在基接口,使得各类节点具有相同的接口,这样就可以让客户端对它们统一处理。
共同操作CompanyProtocol的代码如下:
?
1
2
3
4
5
6
7
8
9
10
#import <Foundation/Foundation.h>
@protocol CompanyProtocol <NSObject>
- (
void
)addCompany:(id<CompanyProtocol>)company;
- (
void
)removeCompany:(id<CompanyProtocol>)company;
- (
void
)display;
@end
共同的操作定义了添加公司、删除公司、展示公司三个方法,我们接着看下组合CompanyComponent类是怎么实现的,代码如下:
?
1
2
3
4
5
6
7
8
9
#import <Foundation/Foundation.h>
#import "CompanyProtocol.h"
@interface CompanyComponent : NSObject <CompanyProtocol>
@property (nonatomic, copy) NSString *companyName;
- (instancetype)initWithCompanyName:(NSString *)companyName;
@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
#import "CompanyComponent.h"
@interface CompanyComponent ()
@property (nonatomic, strong) NSMutableArray *childList;
@end
@implementation CompanyComponent
- (instancetype)initWithCompanyName:(NSString *)companyName {
self = [super init];
if
(self) {
_companyName = companyName;
_childList = [[NSMutableArray alloc] initWithCapacity:0];
}
return
self;
}
- (
void
)addCompany:(id<CompanyProtocol>)company {
[self.childList addObject:company];
}
- (
void
)removeCompany:(id<CompanyProtocol>)company {
[self.childList removeObject:company];
}
- (
void
)display {
NSLog(@
"公司名称:%@"
, self.companyName);
for
(id<CompanyProtocol> company in self.childList) {
[company display];
}
}
@end
CompanyLeaf的代码如下:
?
1
2
3
4
5
6
7
8
9
#import <Foundation/Foundation.h>
#import "CompanyProtocol.h"
@interface CompanyLeaf : NSObject <CompanyProtocol>
@property (nonatomic, copy) NSString *companyName;
- (instancetype)initWithCompanyName:(NSString *)companyName;
@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
#import "CompanyLeaf.h"
@implementation CompanyLeaf
- (instancetype)initWithCompanyName:(NSString *)companyName {
self = [super init];
if
(self) {
_companyName = companyName;
}
return
self;
}
- (
void
)addCompany:(id<CompanyProtocol>)company {
}
- (
void
)removeCompany:(id<CompanyProtocol>)company {
}
- (
void
)display {
NSLog(@
"公司名称:%@"
, self.companyName);
}
@end
从代码中我们可以看到虽然在CompanyLeaf中有addCompany,但是却没有做任何处理,说明CompanyLeaf类不实现这个方法,这样写的好处就是保持了接口的一致性,这样客户端不用去区分组合类型与叶子类型。
客户端代码的调用如下:
?
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
#import "ViewController.h"
#import "CompanyProtocol.h"
#import "CompanyComponent.h"
#import "CompanyLeaf.h"
@interface ViewController ()
@end
@implementation ViewController
- (
void
)viewDidLoad {
[super viewDidLoad];
CompanyComponent *root = [[CompanyComponent alloc] initWithCompanyName:@
"嘟嘟牛科技有限公司"
];
[root addCompany:[[CompanyLeaf alloc] initWithCompanyName:@
"嘟嘟牛人力资源部"
]];
CompanyComponent *component = [[CompanyComponent alloc] initWithCompanyName:@
"深圳视格有限公司(嘟嘟牛子公司)"
];
[component addCompany:[[CompanyLeaf alloc] initWithCompanyName:@
"视格人力资源部"
]];
[root addCompany:component];
NSLog(@
"-----------------结构图----------------"
);
[root display];
}
- (
void
)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}
@end
输出如下:
?
1
2
3
4
5
2015-09-10 22:22:27.493 Component[27847:655455] -----------------结构图----------------
2015-09-10 22:22:27.494 Component[27847:655455] 公司名称:嘟嘟牛科技有限公司
2015-09-10 22:22:27.494 Component[27847:655455] 公司名称:嘟嘟牛人力资源部
2015-09-10 22:22:27.494 Component[27847:655455] 公司名称:深圳视格有限公司(嘟嘟牛子公司)
2015-09-10 22:22:27.495 Component[27847:655455] 公司名称:视格人力资源部
组合模式的主要意图是让树形结构中的每个节点具有相同的抽象接口。这样整个结构可作为一个统一的抽象结构使用,而不暴漏其内部表示。对每个节点(叶节点或组合体)的任何操作,可以通过协议或抽象基类只能怪定义的相同接口来进行。
Demo链接地址:https://github.com/guoshimeihua/Component.git