何为组合模式?git
组合模式让咱们能够把相同基类型的对象组合到树状结构中,其中父节点包含同类型的子节点。换句话说,这种树状结构造成"部分——总体"的层次结构。什么是“部分——总体”的层次结构呢?它是既包含对象的组合又包含叶节点的单个对象的一种层次结构。每一个组合体包含的其余节点,能够是叶节点或者其余组合体。这种关系在这个层次结构中递归重复。由于每一个组合或叶节点有相同的基类型,一样的操做可应用于它们中的每个,而没必要在客户端做类型检查。客户端对组合与叶节点进行操做时可忽略它们之间的差异。github
组合模式:将对象组合成树形结构以表示"部分——总体"的层次结构。组合使得用户对单个对象和组合对象的使用的具备一致性。框架
什么时候使用组合模式?atom
@:想得到对象抽象的树形表示(部分——总体层次结构);spa
@:想让客户端统一处理组合结构中的全部对象。code
在Cocoa Touch框架中使用组合模式component
在Cocoa Touch框架中,UIView被组织成一个组合结构。每一个UIView的实例能够包含UIView的其余实例,造成统一的树形结构。让客户端对单个UIView对象和UIView的组合统一对待。对象
窗口中的UIView在内部造成它的子视图。它们的每个能够包含其余视图而变成本身的子视图的超视图。添加进来的其余UIView成为它的子视图。它们的每个能够包含其余视图而变成本身的子视图的超视图。UIView对象只能有一个超视图,能够有零到多个子视图。递归
视图组合结构参与绘图事件处理。当请求超视图为显示进行渲染时,消息会先在超视图被处理,而后传给其子视图。消息会传播到遍布整个树的其余子视图。由于它们是相同的类型——UIView,它们能够被统一处理。接口
组合模式的实例引用
先看下组合模式的静态结构如图:
基接口是定义了CompanyLeaf类和CompanyComponent类的共同操做的CompanyProtocol。有些操做支队Component有意义,好比addCompany、removeCompany。为什不不把这些方法放在CompanyComponent类中呢?由于咱们不想让客户端在运行时知道它们在处理哪一种类型的节点,也不想把组合结构的内部细节暴漏给客户端。这就是为何虽然操做只对CompanyComponent有意义,咱们仍是把它们声明在基接口,使得各种节点具备相同的接口,这样就可让客户端对它们统一处理。
共同操做CompanyProtocol的代码以下:
#import <Foundation/Foundation.h> @protocol CompanyProtocol <NSObject> - (void)addCompany:(id<CompanyProtocol>)company; - (void)removeCompany:(id<CompanyProtocol>)company; - (void)display; //展现总公司以及子公司 @end
共同的操做定义了添加公司、删除公司、展现公司三个方法,咱们接着看下组合CompanyComponent类是怎么实现的,代码以下:
#import <Foundation/Foundation.h> #import "CompanyProtocol.h" @interface CompanyComponent : NSObject <CompanyProtocol> @property (nonatomic, copy) NSString *companyName; - (instancetype)initWithCompanyName:(NSString *)companyName; @end
#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的代码以下:
#import <Foundation/Foundation.h> #import "CompanyProtocol.h" @interface CompanyLeaf : NSObject <CompanyProtocol> @property (nonatomic, copy) NSString *companyName; - (instancetype)initWithCompanyName:(NSString *)companyName; @end
#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类不实现这个方法,这样写的好处就是保持了接口的一致性,这样客户端不用去区分组合类型与叶子类型。
客户端代码的调用以下:
#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]; // Dispose of any resources that can be recreated. } @end
输出以下:
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] 公司名称:视格人力资源部
组合模式的主要意图是让树形结构中的每一个节点具备相同的抽象接口。这样整个结构可做为一个统一的抽象结构使用,而不暴漏其内部表示。对每一个节点(叶节点或组合体)的任何操做,能够经过协议或抽象基类只能怪定义的相同接口来进行。