文档章节

组合模式(Composite Pattern,对象结构型模式)

翰霖学院
 翰霖学院
发布于 2017/07/24 08:56
字数 2896
阅读 1
收藏 0
点赞 0
评论 0

意图

将对象以树形结构组织起来,以达成“部分-整体”的层次结构,使客户端对单个对象和组合对象的使用具有一致性。
对容器和内容一视同仁,建立递归的结构。

适用性

以下情况使用Composite模式:
1. 你想表示对象的部分—整体层次结构
2. 你希望用户忽略组合对象与单个对象的不同,用户将统一地使用组合结构中的所有对象

结构

这里写图片描述

参与者

Component

为组合中的对象声明接口。
在适当的情况下,实现所有类共有接口的缺省行为。
声明一个接口用于访问和管理Component的子组件
(可选)在递归结构中定义一个接口,用于访问一个父部件,并在合适的情况下实现它。

Leaf

在组合中表示叶节点对象,叶节点没有子节点
在组合中定义图元对象的行为

Composite

定义有子部件的那些部件的行为
存储子部件
在Component接口中实现与子部件有关的操作

Client

通过Component接口中实现与子部件有关的操作。

代码

Component

public interface Component {
    /** * Counts the number of test cases that will be run by this test. */
     public abstract int countTestCases();
     /** * Runs a test and collects its result in a TestResult instance. */
     public abstract void run(String result);
}

Leaf

public class Leaf implements Component {

    public int countTestCases() {
        return 1;
    }
    public void run(String result) {
        System.out.println("run========="+result+"==="+this.countTestCases());
    }
}

Composite

public class Composite implements Component {

    private List<Component> list = new ArrayList<Component>();
    public void addComponent(Component component){
        list.add(component);
    }
    public int countTestCases() {
        return list.size();
    }
    public void run(String result){
        for(Component c : list){
            System.out.println("run========="+result+"========"+c.countTestCases());
        }
    }
}

协作

用户使用Component类接口与组合结构中的对象进行交互。如果接收者是一个叶节点,则直接处理请求。如果接收者是Composite,它通常将请求发送给它的子部件,在转发请求之间与/或之后可能执行一些辅助操作。

效果

定义了包含基本对象和组合对象的类层次结构

基本对象可以被组合成更加复杂的组合对象,而这个组合对象可以被组合,这样不断的递归下去。客户代码中,任何用到基本对象的地方都可以使用组合对象。

简化客户代码

客户可以一致地使用组合结构和单个对象。通常用户不知道(也不关系)处理的是一个叶节点还是一个组合组件。这简化了客户代码,因为在定义组合的那些类中不需要写一些充斥着选择语句的函数。

使得更容易增加新类型的组件

新定义的Composite或Leaf子类自动地与已有的结构和客户代码一起工作,客户程序不需要因新的Component类而改变。

使你的设计变得更加一般化

容易增加新组件也会产生一些问题,那就是很难限制组合中的组件。有时你希望一个组合只能有某些特定的组件。使用Composite时,你不能依赖类型系统施加这些约束,而必须在运行时刻进行检查。

实现

在实现Composite模式时需要考虑以下几个问题:

显式的父部件引用

保持从子部件到父部件的引用能简化组合结构的遍历和管理。父部件引用可以简化结构的上移和组件的删除,同时父部件引用也支持Chain of Responsibility模式。
通常在Component类中定义父部件引用。Leaf和Composite类可以继承这个引用以及管理这个引用的那些操作。
对于父部件引用,必须维护一个不变式,即一个组合的所有子节点以这个组合为父节点,而反之该组合以这个节点为子节点。保证这一点最容易的办法是,仅当在一个组合中增加或删除一个组件时,才改变这个组件的父部件。如果能在Composite的Add和Remove操作中实现这种方法,那么所有的子类都可以继承这一方法,并且将自动维护这一不变式。

共享组件

共享组件是很有用的,比如它减少了对存贮的需求。当时当一个组件只有一个父部件时,很难共享组件。
一个可行的解决办法是为子部件存贮多个父部件,但当一个请求在结构中向上传递时,这种方法会导致多义性。Flyweight模式讨论了如何修改设计以避免将父部件存贮在一起的方法。如果子部件可以将一些状态(或是所有的状态)存储在外部,从而不需要向父部件发送请求,那么这种方法是可行的。

最大化Component

Composite模式的目的之一是使得用户不知道他们正在使用的具体的Leaf和Composite类。为了达到这一目的,Composite类应该为Leaf和Composite类尽可能多定义一些公共操作。Composite类通常为这些操作提供缺省的实现,而Leaf和Composite子类可以对他们进行重定义。
然而,这个目标有可能会与类层次结构设计原则相冲突,该原则规定:一个类只能定义那些对它的子类有意义的操作。有许多Component所支持的操作对Leaf似乎没有什么意义,那么Component怎样为他们提供一个缺省的操作呢?
有时一点创造性可使得一个看起来仅对Composite才有意义的操作,将它移入Component类中,就会对所有的Component都适用。例如,访问子节点的接口是Composite类的一个基本组成部分,但对Leaf类来说并不必要。但是如果我们把一个Leaf看成一个没有子节点的Component,就可以为在Component类中定义一个缺省的操作,用于对子节点进行访问,这个缺省的操作不返回任何一个子节点。Leaf类可以使用缺省的实现,而Composite类则会重新实现这个操作以返回它们的子类。

声明管理子部件的操作(透明性)

虽然Composite类实现了Add和Remove操作用于管理子部件,但是Composite模式中一个重要的问题是:在Composite类层次结构中哪一些类声明这些操作。我们应该在Component中声明这些操作,并是这些操作对Leaf类有意义呢,还是只应该在Composite和它的子类中声明并定义这些操作?
安全性与透明性:
1. 在类层次结构的根部定义子节点管理接口的方法具有良好的透明性,因为你可以一致地使用所有的组件,但是这一方法以些安全性为代价的,因为客户有可能会做一些无意义的事情,例如在Leaf中增加和删除对象等。
2. 在Composite类中定义管理子部件的方法具有良好的安全性,因为在像C++这样的静态类型语言中,在编译时任何从Leaf中增加或删除对象的尝试都会被发现。但是这又损失了透明性,因为Leaf和Composite具有不同的接口。

如果该组件不允许有子部件,或者 R e m o v e的参数不是该组件的子节点时,通常最好使用缺省方式(可能是产生一个异常 )处理A d d和R e m o v e的失败。另一个办法是对“删除”的含义作一些改变。如果该组件有一个父部件引用,我们可重新定义Component :: Remove,在它的父组件中删除掉这个组件。然而,对应的 A d d操作仍然没有合理的解释。

是否应该实现一个Component列表

你可能希望在Component类中将子节点集合定义为一个实例变量,而这个Component类也声明了一些操作对子节点进行访问和管理。但是在基类中存放子类指针,对叶节点来说会导致空间的浪费,因为叶节点根本没有子节点。只有当该结构中子类数据相对较少时,才值得使用这种方法。

子部件排序

许多设计指定了Composite的子部件顺序。如果需要考虑子节点的顺序时,必须仔细的设计对子节点的访问和管理接口,以便管理子节点序列。Iterator模式可以在这方面给予一些指导。

使用高速缓冲存贮改善性能

如果你需要对组合进行频繁的遍历或查找,Composite类可以缓冲存储对它的子节点进行遍历或者查找的相关信息。Composite可以缓冲存储实际结果或者仅仅是一些用于缩短遍历或查询长度的信息。
一个组件发生变化时,它的父部件原先缓冲存贮的信息也变得无效。在组件直到其父部件时,这种方法最为有效。因此,如果你使用高速缓冲存贮,你需要定义一个接口来通知组合组件他们所缓冲存贮的信息无效。

应该由谁删除Component

在没有垃圾回收机制的语言中,当一个Composite被销毁时,通常最好由Composite负责删除其子节点。但是一种情况除外,及Leaf对象不会改变,因此可以被共享。

存贮组件最好用哪一种数据结构

Composite可使用多种数据结构存贮它们的子节点信息,包括连接列表、树、数组和hash表。数据结构的选择决定于效率。事实上,使用通用的数据结构根本没有必要。有时对每个子节点,Composite都有一个变量与之对应,这就要求Composite的每个子类都要实现自己的管理接口。参见Interpreter模式中的例子。

经典例子

树形结构

相关模式

责任链模式

通常部件—父部件连接用于Chain of Responsibility Pattern。

Decorator Pattern

Decorator模式经常与Composite模式一起使用。当装饰和组合一起使用时,他们通常有一个公共的父类。因此装饰必须支持具有Add、Remove、和GetChild操作的Component接口。CompositePattern对容器(Composite参与者)和内容(Leaf参与者)一视同仁,两者都是Component参与者;DecoratorPattern则是对装饰和内容一视同仁

Flyweight Pattern

享元模式让你共享组件,但不能引用他们的父部件。

Iterator Pattern

用来遍历Composite

Visitor Pattern

Visitor将本来应该分布在Composite和Leaf类中的操作和行为局部化。Visitor Patter是在围绕着Composite进行处理时使用。

Command Pattern

在Command Pattern建立宏命令时,会使用到Composite Pattern。
敬请期待“桥梁模式(Bridge Pattern,对象结构型模式)”

© 著作权归作者所有

共有 人打赏支持
翰霖学院
粉丝 0
博文 67
码字总数 45112
作品 0
济南
高级程序员
创建型、结构型、行为型模式(1)

目的 创建型模式 Creational Pattern 结构型模式 Structural Patterns 行为型模式 Behavioral Pattern 概念 创建型模式,就是创建对象的模式,抽象了实例化的过程。它帮助一个系统独立于如何...

晨曦之光
2012/04/24
226
0
一天一种设计模式之八-----组合模式

一.组合模式简介 组合模式属于结构型模式 将组合模式合成树形结构以表示“部分-整体” 的层次结构。“Composite”使得用户对单个对象和组合对象的使用具有一致性。 适用性: 你想表示对象的部...

tongqu
2016/03/15
80
0
设计模式(Design Pattern)系列之.NET专题

最近,不是特别忙,重新翻了下设计模式,特地在此记录一下。会不定期更新本系列专题文章。 设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结...

seayxu
2016/04/16
13
0
结构型模式(1)

6.适配器模式(Adapter) 将一个类的接口转换成客户希望的另一个接口。 Adapter模式使得原本由于接口不兼容而不能一起工作的类可以一起工作 7.桥接(Bridge) 将抽象部分与实现部分分离使他们...

晨曦之光
2012/04/24
52
0
第一章,思想为源.设计模式的概述及分类

一,设计模式概述 关于模式这一概念, 最早出现在城市建筑领域 Christopher Alexander 的一本关于建筑的书(The Timeless Way of Building)。他在书中明确给出了模式的概念: 每一个模式描述了一...

水门-kay
2016/07/23
487
1
设计模式之7个结构型模式

结构型模式概述 结构型模式(Structural Pattern)描述如何将类或者对象结合在一起形成更大的结构,就像搭积木,可以通过简单积木的组合形成复杂的、功能更为强大的结构。 结构型模式概述 结构...

randy_shandong
2017/01/12
0
0
java结构型模式

结构型模式是描述如何将类对象结合在一起,形成一个更大的结构,结构模式描述两种不同的东西:类与类的实例。故可以分为类结构模式和对象结构模式。 在GoF设计模式中,结构型模式有: 1.适配...

就是小灰灰
2017/07/17
0
0
合成模式(Composite)-结构型

原理 合成模式属于对象的结构模式,有时又叫做“部分——整体”模式。合成模式将对象组织到树结构中,可以用来描述整体与部分的关系。合成模式可以使客户端将单纯元素与复合元素同等看待。 ...

jephon
2016/08/25
0
0
Java中的24种设计模式与7大原则

1,创建型模式 一、抽象工厂模式(Abstract factory pattern): 提供一个接口, 用于创建相关或依赖对象的家族, 而不需要指定具体类. 二、生成器模式(Builder pattern): 使用生成器模式封装一个...

OrionBox
2012/09/12
0
0
PHP之设计模型分类(一)

经典的《设计模式》一书归纳出23种设计模式,本文按《易学设计模式》一书归纳分类如下: 1.创建型模式 前面讲过,社会化的分工越来越细,自然在软件设计方面也是如此,因此对象的创建和对象...

peasant
2016/04/29
81
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

什么是Base64

一、什么是Base64? 百度百科中对Base64有一个很好的解释:“Base64是网络上最常见的用于传输8Bit字节码的编码方式之一,Base64就是一种基于64个可打印字符来表示二进制数据的方法”。 什么是...

Jack088
6分钟前
0
0
SQL多表联查leftjoin左边加表单

SELECT IFNULL(u.USER_ACCOUNT, o.USER_ACCOUNT) u.USER_ACCOUNT, o.* FROM gh_orders o LEFT JOIN gh_user u ON o.PARENT_ID = u.ROW_ID 1.假如u.USER_ACCOUNT不空返回u.USER_ACCOUNT,否则返......

森火
10分钟前
0
0
expect脚本同步文件、expect脚本指定host和要同步的文件、构建文件分发系统

expect脚本同步文件 更改权限 执行脚本 查看执行结果 expect eof需要加上,作用是等脚本命令执行完再进行退出 expect脚本指定host和要同步的文件 更改权限,执行脚本 构建文件分发系统 需求背...

Zhouliang6
48分钟前
1
0
Hive应用:外部分区表

Hive应用:外部分区表 介绍 Hive可以创建外部分区表。创建表的时候,分区要在建表语句中体现。建完之后,你不会在表中看到数据,需要进行分区添加,使用alter语句进行添加。然后数据才会显示...

星汉
59分钟前
3
0
点击Enter登录

1. 效果 2. 实现过程(记得引入jq文件) //6.回车事件 登录 $(function() { document.onkeydown = function(event) { var e = event || window.event || arguments.callee.caller.arguments......

Lucky_Me
今天
1
0
点击菜单内容切换

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <style> .menu{ height: 38px; background-color: #eeeeee; line-height: 38px; } .mao{ ......

南桥北木
今天
1
0
OSChina 周六乱弹 —— 妹子和游戏哪个更好玩

Osc乱弹歌单(2018)请戳(这里) 【今日歌曲】 @andonny :分享唐朝乐队的单曲《国际歌》 《国际歌》- 唐朝乐队 手机党少年们想听歌,请使劲儿戳(这里) @举个栗子- :日常祈雨 邪恶的大祭...

小小编辑
今天
613
8
流利阅读笔记32-20180721待学习

“人工智能”造假:只有人工,没有智能 Lala 2018-07-21 1.今日导读 当今社会,擅长单个方面的人工智能已经盛行,手机借助 AI 智慧防抖技术帮助大家拍出清晰照片,谷歌研发的 AI 助手将可以帮...

aibinxiao
今天
10
0
我的成长记录(一)

今天突然精神抖擞,在我的博客下新开一项分类>成长记录,专门记录每隔一段时间我的一点感悟吧。因为今天才专门花时间新开这样一个分类,所以以前有过的一些感悟没有记录下来,现在已经想不起...

dtqq
今天
1
0
机器学习管理平台 MLFlow

最近工作很忙,博客一直都没有更新。抽时间给大家介绍一下Databrick开源的机器学习管理平台-MLFlow。 谈起Databrick,相信即使是不熟悉机器学习和大数据的工程湿们也都有所了解,它由Spark的...

naughty
今天
19
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部