组合模式(Composite Pattern)

来源:互联网 发布:打印机无法网络打印 编辑:程序博客网 时间:2024/06/05 20:57

定义

组合模式将具有相同的基本类型的对象组合成树形结构的对象,该树的父节点和子节点具有相同的类型,相同的接口。换句话说,将对象组合成树形结构以表示“部分-整体”的层次结构,Composite使得用户对单个对象和组合对象的使用具有一致性。

由于父节点和子节点具有相同的基本类型,所以在整个树上不需要做任何类型检查,客户端就可以在父节点和子节点上进行相同的操作,而不需要区分它所需要操作的对象是父节点还是子节点。使用组合对象的客户端可以忽略树的父节点和字节点得差异,使得用起来非常顺手、简单,下面是一个运行时的组合对象结构的示例:


上述的组合对象是一个树形结构,不过并非一个二叉树,每个对象都是具有相同的接口,使得客户端看起来并无差异。类似的组合模式的静态结构类图如下图所示:


Component所定义的接口是类LeafComposite共享的。从上图的定义来看,显然有些接口Leaf并没有对应的意义,所以在图中的Leaf中并没有看到Component的全部接口,但是,你别以为Leaf不支持这些接口。虽然有些接口只有在Composite类里面有意义,比如上述类图中的add:Componentremove:Component等,但是这不妨碍类Leaf共享该接口,只不过类Leaf实现该方法是使用空方法而已(因为这些接口对Leaf毫无意义,只是为了和整个树保持统一而已)。

树的每个节点或表示一个叶子节点,或表示一个组合节点,他们的主要区别在于叶子节点没有组合节点的子节点。但是,因为叶子节点和组合节点共享一套接口,所以任何属于Component的操作都可以安全地适用于叶子节点和组合节点。

关于组合设计模式,其原定义如下所示:



适用场景

你如果有以下需求,不妨考虑考虑:

  • 你希望客户端忽略组合对象与单个对象的不同,而是能够统一地使用组合结构中的所有对象
  • 你希望表现出对象-层次的层次结构
使用组合设计模式,可以帮助你:
  • 通过定义包括基本对象和组合对象的层次结构,你可以使用简单的基本对象组合晨较为复杂的组合对象,而你还可以继续使用较为复杂的组合对象组合成更为复杂的对象,如此递归循环,你可以组成自己所需要的更复杂的结构对象。但是,对于客户端而言,使用简单对象和使用复杂组合对象是无差别的
  • 简化客户单代码,同时使得创建同类型的复杂对象更简单。因为客户端不需要区分单个对象还是组合对象,所以不必写if-else之类的各种判断,而新对象也只需组合即可
但是,组合模式使得全部对象统一化了,如果你想为某些组合对象开个小灶,做点特殊处理,那就比较麻烦了。

Cocoa Touch中的组合模式

在Cocoa Touch框架中,UIViews是使用组合模式的树形结构组织管理的。
每一个UIView对象都可以包含其他的UIView对象以构建一颗统一的树结构,使得客户端可以一同对待单个UIView对象和组合UIView对象。IOS程序中,window内的UIView对象是一颗内建的树形结构,其中树的根是UIWindow对象所包含的UIView对象,其他的UIView对象是它的子视图。虽然其他的是子视图,但是任何UIView对象都可以通过往该视图里面添加子视图而转变为父视图,任何UIView对象!UIView对象只能有一个父视图,但是允许有任意多个子视图。(树不就是这样?)UIView视图管理示意图如下所示:


上图的关系图可以使用更为清晰的树形示意图表示为:


上述的view组合结构扮演者两个角色:绘画和事件处理。当一个UIView需要渲染显示的时候,该消息会递归式地向其父视图传递,因此先是父视图处理渲染显示消息,而后才是子视图处理。
组成树形结构的view对象也是事件处理和动作消息的响应链。

代码示例

本例是按上面所述的几个图结构来看看如何使用组合设计模式的,其中Component因为IOS里面已经定义了,所以使用UDComponent来替代,其定义和实现如下所示:
////  Component.h//  CompositeDemo////  Created by God Lin on 15/1/25.//  Copyright (c) 2015年 arbboter. All rights reserved.//#import <Foundation/Foundation.h>@interface UDComponent : NSObject- (void) operation;- (void) add:(UDComponent*)component;- (void) remove:(UDComponent*)component;- (NSInteger) getCount;@end
////  Component.m//  CompositeDemo////  Created by God Lin on 15/1/25.//  Copyright (c) 2015年 arbboter. All rights reserved.//#import "Component.h"@interface UDComponent ()@property (nonatomic, strong) NSMutableArray* children;@end@implementation UDComponent- (id) init{    if(self = [super init])    {        _children = [[NSMutableArray alloc] init];    }    return self;}- (void) operation;{    NSLog(@"...do nothing.");}- (void) add:(UDComponent*)component{    [self.children addObject:component];}- (void) remove:(UDComponent*)component{    [self.children removeObject:component];}- (NSInteger) getCount{    NSInteger sum = 1;        for (UDComponent* c in self.children)    {        sum += [c getCount];    }    return sum;}@end
这里的UDComponent定义了一些默认的方法,子类可选择是否直接使用,不过需要对应的声明变量_children.
Comosite的定义实现如下,该对象是组合对象:
////  Composite.m//  CompositeDemo////  Created by God Lin on 15/1/25.//  Copyright (c) 2015年 arbboter. All rights reserved.//#import "Composite.h"@interface Composite ()// 为了简示Composite和Component内部的不同,定义不同的属性变量// 存储子节点@property (nonatomic, strong) NSMutableArray* sons;@end// 如果这里仍然像UDComponent使用children存储子节点,// 那么这里可以直接使用父类的方法,不需要自己写// 示例为了表现出内部的复杂性@implementation Composite- (id) init{    if(self = [super init])    {        _sons = [[NSMutableArray alloc] init];    }    return self;}- (void) operation;{    NSLog(@"child node count -> %ld", self.sons.count);    for (UDComponent* c in self.sons)    {        [c operation];    }}- (void) add:(UDComponent*)component{    [self.sons addObject:component];}- (void) remove:(UDComponent*)component{    [self.sons removeObject:component];}- (NSInteger) getCount{    NSInteger sum = 1;        for (UDComponent* c in self.sons)    {        sum += [c getCount];    }    return sum;}@end
Composite重新自实现了一遍。
Leaf定义实现如下:
////  Leaf.m//  CompositeDemo////  Created by God Lin on 15/1/25.//  Copyright (c) 2015年 arbboter. All rights reserved.//#import "Leaf.h"// 无子节点,所以不需要定义存储子节点的变量@implementation Leaf- (void) operation;{    NSLog(@"leaf node, no child");}- (void) add:(UDComponent*)component{    // 该方法对Leaf无意义,空白实现}- (void) remove:(UDComponent*)component{    // 该方法对Leaf无意义,空白实现}- (NSInteger) getCount{    return 1;}@end
客户端测试使用的代码如下:
////  main.m//  CompositeDemo////  Created by God Lin on 15/1/23.//  Copyright (c) 2015年 arbboter. All rights reserved.//#import <Foundation/Foundation.h>#import "Leaf.h"#import "Composite.h"int main(int argc, const char * argv[]){    @autoreleasepool    {        // 生成本章的运行时对象结构图        // 从下往上生成        Composite* composite = nil;        Leaf* leaf = [[Leaf alloc] init];                composite = [[Composite alloc] init];        [composite add:leaf];        [composite add:[[Leaf alloc] init]];        [composite add:[[Leaf alloc] init]];                Composite* compositeFather = nil;        compositeFather = [[Composite alloc] init];        [compositeFather add:composite];        [compositeFather add:[[Leaf alloc] init]];                composite = compositeFather;        compositeFather = [[Composite alloc] init];        [compositeFather add:[[Leaf alloc] init]];        [compositeFather add:[[Leaf alloc] init]];        [compositeFather add:composite];        [compositeFather add:[[Leaf alloc] init]];                // 客户端使用组合对象        // 组合        [compositeFather operation];        // 单个对象        [leaf operation];    }    return 0;}
可以看到,使用组合对象不管是构建一颗有层次的树,还是客户端可以简单地使用组合对象的对象(不需要区分Composite和Leaf对象),组合模式都有非常大得优势。

总结

组和模式可以创建有层次结构的对象,同时降低了客户端和复杂的内部结构的耦合性,简单粗暴地使得单个对象和组合对象共享一套接口。

0 0
原创粉丝点击