文档章节

iOS设计模式——组合模式

国士梅花
 国士梅花
发布于 2015/09/10 22:56
字数 1598
阅读 740
收藏 5

何为组合模式?

    组合模式让我们可以把相同基类型的对象组合到树状结构中,其中父节点包含同类型的子节点。换句话说,这种树状结构形成"部分——整体"的层次结构。什么是“部分——整体”的层次结构呢?它是既包含对象的组合又包含叶节点的单个对象的一种层次结构。每个组合体包含的其他节点,可以是叶节点或者其他组合体。这种关系在这个层次结构中递归重复。因为每个组合或叶节点有相同的基类型,同样的操作可应用于它们中的每一个,而不必在客户端作类型检查。客户端对组合与叶节点进行操作时可忽略它们之间的差别。

    组合模式:将对象组合成树形结构以表示"部分——整体"的层次结构。组合使得用户对单个对象和组合对象的使用的具有一致性。

何时使用组合模式?

    @:想获得对象抽象的树形表示(部分——整体层次结构);

    @:想让客户端统一处理组合结构中的所有对象。

在Cocoa Touch框架中使用组合模式

    在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] 公司名称:视格人力资源部

    组合模式的主要意图是让树形结构中的每个节点具有相同的抽象接口。这样整个结构可作为一个统一的抽象结构使用,而不暴漏其内部表示。对每个节点(叶节点或组合体)的任何操作,可以通过协议或抽象基类只能怪定义的相同接口来进行。

    Demo链接地址:https://github.com/guoshimeihua/Component.git

© 著作权归作者所有

国士梅花
粉丝 10
博文 29
码字总数 53560
作品 0
深圳
程序员
私信 提问
移动开发之设计模式- 代理模式(IOS&Android)

资源 完全参照 代理模式|菜鸟教程但不包括IOS代码 代理模式 在代理模式(Proxy Pattern)中,一个类代表另一个类的功能。这种类型的设计模式属于结构型模式。 在代理模式中,我们创建具有现有...

FlanneryZJ
2018/12/18
0
0
如何判断你是合格的高级iOS开发工程师?

前言 随着移动互联网的高速发展泄洪而来,有意学习移动开发的人越来越多了,竞争也是越来越大,需要学习的东西很多。如何才能在激烈的移动开发者竞争中一枝独秀,成为一名真正合格的高级iOS...

_小迷糊
2018/05/26
0
0
设计模式2——Factory设计模式

Factory工厂设计模式为创建对象提供了一种抽象,而对使用者屏蔽了对象创建的具体细节过程,工厂模式有三种:简单工厂模式,抽象工厂模式和工厂方法模式。 1. 简单工厂模式: 又叫静态工厂模式...

小米米儿小
2013/12/05
172
0
移动开发之设计模式-工厂模式(IOS&Android)

资源 完全参照 工厂模式|菜鸟教程 ,但不包括IOS代码 工厂模式 工厂模式(Factory Pattern)是 Java 中最常用的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳...

FlanneryZJ
2018/12/17
0
0
【干货】iOS开发学习中不易弄清楚的六种开发模式

在我们的实际开发中,架构模式多多少少也有几十种,在很多模式里,相信很多刚开始接触ios的程序员会遇到这类问题,本文小编就为大家详细解说这6种模式应用场景,帮助大家理清思路,学好ios开...

原来是泽镜啊
2018/04/30
20
0

没有更多内容

加载失败,请刷新页面

加载更多

CentOS7.6中安装使用fcitx框架

内容目录 一、为什么要使用fcitx?二、安装fcitx框架三、安装搜狗输入法 一、为什么要使用fcitx? Gnome3桌面自带的输入法框架为ibus,而在使用ibus时会时不时出现卡顿无法输入的现象。 搜狗和...

技术训练营
昨天
5
0
《Designing.Data-Intensive.Applications》笔记 四

第九章 一致性与共识 分布式系统最重要的的抽象之一是共识(consensus):让所有的节点对某件事达成一致。 最终一致性(eventual consistency)只提供较弱的保证,需要探索更高的一致性保证(stro...

丰田破产标志
昨天
8
0
docker 使用mysql

1, 进入容器 比如 myslq1 里面进行操作 docker exec -it mysql1 /bin/bash 2. 退出 容器 交互: exit 3. mysql 启动在容器里面,并且 可以本地连接mysql docker run --name mysql1 --env MY...

之渊
昨天
10
0
python数据结构

1、字符串及其方法(案例来自Python-100-Days) def main(): str1 = 'hello, world!' # 通过len函数计算字符串的长度 print(len(str1)) # 13 # 获得字符串首字母大写的...

huijue
昨天
6
0
PHP+Ajax微信手机端九宫格抽奖实例

PHP+Ajax结合lottery.js制作的一款微信手机端九宫格抽奖实例,抽奖完成后有收货地址添加表单出现。支持可以设置中奖概率等。 奖品列表 <div class="lottery_list clearfix" id="lottery"> ......

ymkjs1990
昨天
5
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部