组合模式(Composite Pattern)

定义

组合模式将具有相同的基本类型的对象组合成树形结构的对象,该树的父节点和子节点具有相同的类型,相同的接口。换句话说,将对象组合成树形结构以表示“部分-整体”的层次结构,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对象),组合模式都有非常大得优势。

总结

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值