看图识模式
这里有个OA系统的结构,如下:
图一
如果公司发展,在全国开了许多分公司,每个分公司都有自己的人力、财务、业务等部门。然后每个分公司下面又设置自己的办事处,每个办事处都也有这些部门,如下:
图二
这些新增加的分公司、办事处都需要用这套OA系统,该怎么设计呢?
如果让每个分公司都用这套系统,根据ID判断所属的公司展示对应的部门;问题这样就不能展示树状结构图,不同级别的公司也不可以简单的平行管理,而且每一个节点都要判断对应的单位然后调用对应的方法。
分析
这里我们可以发现分公司和办事处和总公司的关系就是部分和整体的关系。
总公司的组织架构可以复用于分公司这其实就是
整体与部分可以被一致对待
的问题
如果把公司总部当做一颗大树的根部,它的下属分公司其实就是这棵树的分枝,各个办事处就是更小的分枝,而相关的职能部门由于没有分枝了,它们可以理解为叶子。
尽管天下没有两片相同的树叶,但是同一棵树上长出的叶子也不会差到哪去。也就是说,总部的财务部管理功能也最好复用到子公司,最好的办法就是它们的财务管理功能的方法都是一样的。
模式定义
组合模式: 将对象组合成树行结构以表示‘部分-整体’的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。
组合模式将具有相同的基本类型的对象组合成树形结构的对象,该树的父节点和子节点具有相同的类型,相同的接口。换句话说,将对象组合成树形结构以表示“部分-整体”的层次结构,Composite使得用户对单个对象和组合对象的使用具有一致性。
由于父节点和子节点具有相同的基本类型,所以在整个树上不需要做任何类型检查,客户端就可以在父节点和子节点上进行相同的操作,而不需要区分它所需要操作的对象是父节点还是子节点。使用组合对象的客户端可以忽略树的父节点和字节点得差异,使得用起来非常顺手、简单。
上图二的结构就可以这样设计了:
composite: 总公司、分公司、办事处
leaf: 职能部门
注意:
上述的组合对象是一个树形结构,不过并非一个二叉树,每个对象都是具有相同的接口,使得客户端看起来并无差异。
结构图
静态结构类图:
Component所定义的接口是类Leaf和Composite共享的。从上图的定义来看,显然有些接口Leaf并没有对应的意义,所以在图中的Leaf中并没有看到Component的全部接口,但是,不要以为Leaf不支持这些接口。虽然有些接口只有在Composite类里面有意义,比如上述类图中的add:Component、remove:Component等,但是这不妨碍类Leaf共享该接口,只不过类Leaf实现该方法是使用空方法而已(因为这些接口对Leaf毫无意义,只是为了和整个树保持统一而已)。
树的每个节点或表示一个叶子节点,或表示一个组合节点,他们的主要区别在于叶子节点没有组合节点的子节点。但是,因为叶子节点和组合节点共享一套接口,所以任何属于Component的操作都可以安全地适用于叶子节点和组合节点。
适用场景
忽略组合对象与单个对象的不同,而是能够统一地使用组合结构中的所有对象
希望表现出对象-层次的层次结构
使用组合设计模式:
可以使用简单的基本对象组合成较为复杂的组合对象,复杂组合对象又可以组合成更为复杂的对象,如此递归循环。但是使用简单对象和使用复杂组合对象是无差别的
简化客户单代码,同时使得创建同类型的复杂对象更简单。因为客户端不需要区分单个对象还是组合对象,所以不必写if-else之类的各种判断
CocoaTouch中的组合模式
在Cocoa Touch框架中,UIView对象被组合成一个树形结构,UIView对象可以包含其他的UIView对象。这种组合方式便于统一用于事件处理,例如处理渲染事件时,事件会在父视图中被处理,然后在传递给子视图,因为他们都是相同的类型,事件可以传递到树形结构的每一视图。
代码示例
咱们刚开始的问题现在看看如何设计:
具体代码:
Company类(这是一个抽象类)
/// Abstract class
/// 这是一个抽象类
@interface Company : NSObject
@property (nonatomic, copy) NSString *name;
- (instancetype)initWithName:(NSString *)name;
- (void)add:(Company *)company;
- (void)remove:(Company *)company;
- (void)display;
- (void)lineOfDuty;
@end
@implementation Company
- (instancetype)initWithName:(NSString *)name
{
self = [super init];
if (self) {
_name = name;
}
return self;
}
- (void)add:(Company *)company
{
NSAssert(NO, @"子类必须重写");
}
- (void)remove:(Company *)company
{
NSAssert(NO, @"子类必须重写");
}
- (void)display
{
NSAssert(NO, @"子类必须重写");
}
- (void)lineOfDuty
{
NSAssert(NO, @"子类必须重写");
}
@end
ConcreteCompany类
@interface ConcreteCompany ()
@property (nonatomic, strong) NSMutableArray *children;
@end
@implementation ConcreteCompany
- (instancetype)initWithName:(NSString *)name
{
self = [super init];
if (self) {
self.name = name;
_children = [NSMutableArray array];
}
return self;
}
- (void)add:(Company *)company
{
if (!company) {
return;
}
[self.children addObject:company];
}
- (void)remove:(Company *)company
{
if (!company) {
return;
}
[self.children removeObject:company];
}
- (void)display
{
NSLog(@"---%@:%@ \n", NSStringFromClass([self class]), self.name);
for (Company *company in self.children) {
[company display];
}
}
- (void)lineOfDuty
{
for (Company *company in self.children) {
[company lineOfDuty];
}
}
@end
HRDepartment类
@implementation HRDepartment
- (void)add:(Company *)company
{
}
- (void)remove:(Company *)company
{
}
- (void)display
{
NSLog(@"%@", self.name);
}
- (void)lineOfDuty
{
NSLog(@"%@: %@", self.name, @"员工招聘培训管理");
}
@end
FinanceDepartment类
@implementation FinanceDepartment
- (void)add:(Company *)company
{
}
- (void)remove:(Company *)company
{
}
- (void)display
{
NSLog(@"%@", self.name);
}
- (void)lineOfDuty
{
NSLog(@"%@: %@", self.name, @"公司财务收支管理");
}
@end
Client调用
ConcreteCompany *root = [[ConcreteCompany alloc] initWithName:@"上海总公司"];
[root add:[[HRDepartment alloc] initWithName:@"总公司人力资源部"]];
[root add:[[FinanceDepartment alloc] initWithName:@"总公司财务部"]];
ConcreteCompany *tianjinCompany = [[ConcreteCompany alloc] initWithName:@"天津分公司"];
[tianjinCompany add:[[HRDepartment alloc] initWithName:@"天津分公司人力资源部"]];
[tianjinCompany add:[[FinanceDepartment alloc] initWithName:@"天津分公司财务部"]];
[root add:tianjinCompany];
ConcreteCompany *nanjinAgency = [[ConcreteCompany alloc] initWithName:@"南京办事处"];
[nanjinAgency add:[[HRDepartment alloc] initWithName:@"南京办事处人力资源部"]];
[nanjinAgency add:[[FinanceDepartment alloc] initWithName:@"南京办事处财务部"]];
[tianjinCompany add:nanjinAgency];
ConcreteCompany *wuhanAgency = [[ConcreteCompany alloc] initWithName:@"武汉办事处"];
[wuhanAgency add:[[HRDepartment alloc] initWithName:@"武汉办事处人力资源部"]];
[wuhanAgency add:[[FinanceDepartment alloc] initWithName:@"武汉办事处财务部"]];
[tianjinCompany add:wuhanAgency];
NSLog(@"------------------结构图\n");
[root display];
NSLog(@"------------------职能\n");
[root lineOfDuty];
输出结果:
2017-01-17 23:08:48.805 ComponentDemo[10508:507549] ------------------结构图
2017-01-17 23:08:48.805 ComponentDemo[10508:507549] ---ConcreteCompany:上海总公司
2017-01-17 23:08:48.806 ComponentDemo[10508:507549] 总公司人力资源部
2017-01-17 23:08:48.806 ComponentDemo[10508:507549] 总公司财务部
2017-01-17 23:08:48.806 ComponentDemo[10508:507549] ---ConcreteCompany:天津分公司
2017-01-17 23:08:48.806 ComponentDemo[10508:507549] 天津分公司人力资源部
2017-01-17 23:08:48.806 ComponentDemo[10508:507549] 天津分公司财务部
2017-01-17 23:08:48.806 ComponentDemo[10508:507549] ---ConcreteCompany:南京办事处
2017-01-17 23:08:48.807 ComponentDemo[10508:507549] 南京办事处人力资源部
2017-01-17 23:08:48.807 ComponentDemo[10508:507549] 南京办事处财务部
2017-01-17 23:08:48.807 ComponentDemo[10508:507549] ---ConcreteCompany:武汉办事处
2017-01-17 23:08:48.807 ComponentDemo[10508:507549] 武汉办事处人力资源部
2017-01-17 23:08:48.807 ComponentDemo[10508:507549] 武汉办事处财务部
2017-01-17 23:08:48.808 ComponentDemo[10508:507549] ------------------职能
2017-01-17 23:08:48.808 ComponentDemo[10508:507549] 总公司人力资源部: 员工招聘培训管理
2017-01-17 23:08:48.821 ComponentDemo[10508:507549] 总公司财务部: 公司财务收支管理
2017-01-17 23:08:48.822 ComponentDemo[10508:507549] 天津分公司人力资源部: 员工招聘培训管理
2017-01-17 23:08:48.822 ComponentDemo[10508:507549] 天津分公司财务部: 公司财务收支管理
2017-01-17 23:08:48.822 ComponentDemo[10508:507549] 南京办事处人力资源部: 员工招聘培训管理
2017-01-17 23:08:48.822 ComponentDemo[10508:507549] 南京办事处财务部: 公司财务收支管理
2017-01-17 23:08:48.823 ComponentDemo[10508:507549] 武汉办事处人力资源部: 员工招聘培训管理
2017-01-17 23:08:48.823 ComponentDemo[10508:507549] 武汉办事处财务部: 公司财务收支管理
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。