基本概念
组合模式(Composite),将对象组合成树形结构以表示‘部分-整体’的层次结构。组合模式使得用户对单个对象和组合的对象的使用具有了一致性。
component为组合中的对象声明接口,在适当情况下,实现所有类共有接口的默认行为。
leaf在组合中表示叶节点对象,叶节点没有子节点。
composite定义有子节点,用来存储字部分,在component接口中实现与字部分有关的操作。
代码实现
公司类抽象
@interface Company : NSObject
@property (nonatomic, copy)NSString *name;
@property (nonatomic, strong)NSMutableArray *childrenArray;
- (instancetype)initWithName:(NSString *)name;
- (void)add:(Company *)company;
- (void)remove:(Company *)company;
- (void)display:(int)depth;//显示
- (void)lineOfDuty;// 履行职责
#import "Company.h"
@interface Company()
@end
@implementation Company
- (instancetype)initWithName:(NSString *)name
{
self = [super init];
if (self) {
self.name = name;
self.childrenArray = [NSMutableArray array];
}
return self;
}
具体公司类
#import "ConcreteCompany.h"
@implementation ConcreteCompany
- (void)add:(Company *)company
{
[self.childrenArray addObject:company];
}
- (void)remove:(Company *)company
{
if ([self.childrenArray containsObject:company]) {
[self.childrenArray removeObject:company];
}
}
- (void)display:(int)depth
{
NSLog(@"%d-%@",depth,self.name);
for (Company *component in self.childrenArray) {
[component display:(depth + 2)];
}
}
- (void)lineOfDuty
{
for (Company *component in self.childrenArray) {
[component lineOfDuty];
}
}
@end
人力资源部和财务部
(1)人力
#import "HRDepartment.h"
@implementation HRDepartment
- (void)add:(Company *)company
{
}
- (void)remove:(Company *)company
{
}
- (void)display:(int)depth
{
NSLog(@"%d-%@",depth,self.name);
}
- (void)lineOfDuty
{
NSLog(@"%@ 员工招聘培训管理",self.name);
}
@end
(财务)
#import "FinanceDepartment.h"
@implementation FinanceDepartment
- (void)add:(Company *)company
{
}
- (void)remove:(Company *)company
{
}
- (void)display:(int)depth
{
NSLog(@"%d-%@",depth,self.name);
}
- (void)lineOfDuty
{
NSLog(@"%@ 公司财务收支管理",self.name);
}
@end
客户端调用
ConcreteCompany *root = [[ConcreteCompany alloc] initWithName:@"北京总公司"];
[root add:[[HRDepartment alloc]initWithName:@"总公司人力资源部"]];
[root add:[[FinanceDepartment alloc]initWithName:@"总公司财务部"]];
ConcreteCompany *comp = [[ConcreteCompany alloc] initWithName:@"上海华东分公司"];
[comp add:[[HRDepartment alloc]initWithName:@"华东分公司人力资源部"]];
[comp add:[[FinanceDepartment alloc]initWithName:@"华东分公司财务部"]];
[root add:comp];
ConcreteCompany *comp1 = [[ConcreteCompany alloc] initWithName:@"南京办事处"];
[comp1 add:[[HRDepartment alloc]initWithName:@"南京办事处人力资源部"]];
[comp1 add:[[FinanceDepartment alloc]initWithName:@"南京办事处财务部"]];
[comp add:comp1];
ConcreteCompany *comp2 = [[ConcreteCompany alloc] initWithName:@"杭州办事处"];
[comp2 add:[[HRDepartment alloc]initWithName:@"杭州办事处人力资源部"]];
[comp2 add:[[FinanceDepartment alloc]initWithName:@"杭州办事处财务部"]];
[comp add:comp2];
[root display:1];
[root lineOfDuty];
输出
2017-04-28 10:56:51.191 PlayTry[1942:63351] 1-北京总公司
2017-04-28 10:56:51.191 PlayTry[1942:63351] 3-总公司人力资源部
2017-04-28 10:56:51.192 PlayTry[1942:63351] 3-总公司财务部
2017-04-28 10:56:51.192 PlayTry[1942:63351] 3-上海华东分公司
2017-04-28 10:56:51.192 PlayTry[1942:63351] 5-华东分公司人力资源部
2017-04-28 10:56:51.193 PlayTry[1942:63351] 5-华东分公司财务部
2017-04-28 10:56:51.193 PlayTry[1942:63351] 5-南京办事处
2017-04-28 10:56:51.193 PlayTry[1942:63351] 7-南京办事处人力资源部
2017-04-28 10:56:51.193 PlayTry[1942:63351] 7-南京办事处财务部
2017-04-28 10:56:51.194 PlayTry[1942:63351] 5-杭州办事处
2017-04-28 10:56:51.194 PlayTry[1942:63351] 7-杭州办事处人力资源部
2017-04-28 10:56:51.194 PlayTry[1942:63351] 7-杭州办事处财务部
2017-04-28 10:56:51.195 PlayTry[1942:63351] 总公司人力资源部 员工招聘培训管理
2017-04-28 10:56:51.195 PlayTry[1942:63351] 总公司财务部 公司财务收支管理
2017-04-28 10:56:51.196 PlayTry[1942:63351] 华东分公司人力资源部 员工招聘培训管理
2017-04-28 10:56:51.222 PlayTry[1942:63351] 华东分公司财务部 公司财务收支管理
2017-04-28 10:56:51.224 PlayTry[1942:63351] 南京办事处人力资源部 员工招聘培训管理
2017-04-28 10:56:51.224 PlayTry[1942:63351] 南京办事处财务部 公司财务收支管理
2017-04-28 10:56:51.226 PlayTry[1942:63351] 杭州办事处人力资源部 员工招聘培训管理
2017-04-28 10:56:51.226 PlayTry[1942:63351] 杭州办事处财务部 公司财务收支管理
总结
将 对 象 组 合 成 树 形 结 构 以 表 示 “ 部 分 -整 体 ” 的 层 次 结 构 。 组合模式 使 得 用 户 对 单 个 对 象 和组合对象的使用具有一致性。
组合模式的目的就是让客户端不用区分操作的对象是子节点还是叶节点,而是用一种统一的方式来操作。
实现这个目标的关键在于,设计一个抽象的组件类,让它可以代码子节点和叶节点,这样客户端就不需要区分二者,统一操作它们即可。
通常,组合模式都是用树形结构来表示的,通过根节点、子节点、叶节点组合成一颗对象树,这也意味着任何可以使用对象树来描述或者操作的功能,都可以使用组合模式来进行,比如XML解析,层次结构的菜单,iOS中由多个子视图构成的复杂视图,这些都可以使用组合模式来实现。
同时要注意,因为要让客户端统一操作子节点和叶节点,那么他们的抽象类就必须定义包含二者的所有方法,如果调用者调用了它们不支持的方法,可以抛出异常警告。这虽然违反了单一原则,但是在实际开发中是合理的。
好处
定义了包含基本对象和组合对象的类层次结构
基本对象可以被组合成更复杂的组合对
象,而这个组合对象又可以被组合,这样不断的递归下去。客户代码中,任何用到基本
对象的地方都可以使用组合对象。
简化客户代码
客户可以一致地使用组合结构和单个对象。通常用户不知道 (也不关心)处理的是一个叶节点还是一个组合组件。这就简化了客户代码 , 因为在定义组合的那些类中不需要写一些充斥着选择语句的函数。
使 得 更 容 易 增 加 新 类 型 的 组 件
新定义的 C o m p o s i t e 或 L e a f 子 类 自 动 地 与 已 有 的 结 构 和 客
户代码一起工作,客户程序不需因新的 C o m p o n e n t类而改变。
使你的设计变得更加一般化
容易增加新组件也会产生一些问题,那就是很难限制组合
中的组件。有时你希望一个组合只能有某些特定的组件。使用 C o m p o s i t e 时,你不能依赖类型系统施加这些约束,而必须在运行时刻进行检查。
使用时机
如果你想表示对象的部分--整体层次结构。可以选用组合模式把整体和部分统一起来,使得层次结构实现更简单,从外部使用这个层次结构也更容易。
如果你希望统一的使用组合结构中的所有对象,这正是组合模式提供的主要功能