Java 设计模式系列(九)组合模式

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

一、组合模式结构

  • Component: 抽象的组件对象,为组合中的对象声明接口,让客户端可以通过这个接口来访问和管理整个对象结构,可以在里面为定义的功能提供缺省的实现。

  • Leaf: 叶子节点对象,定义和实现叶子对象的行为,不再包含其它的子节点对象。

  • Composite: 组合对象,通常会存储子组件,定义包含子组件的那些组件的行为,并实现在组件接口中定义的与子组件有关的操作。

  • Client: 客户端,通过组件接口来操作组合结构里面的组件对象。

组合模式参考实现

(1)先看看组件对象的定义,示例代码如下:

/**
* 抽象的组件对象,为组合中的对象声明接口,实现接口的缺省行为
*/
public abstract class Component { /** 示意方法,子组件对象可能有的功能方法 */
public abstract void someOperation(); /** 向组合对象中加入组件对象 */
public void addChild(Component child) {
// 缺省的实现,抛出例外,因为叶子对象没有这个功能,或者子组件没有实现这个功能
throw new UnsupportedOperationException("对象不支持这个功能");
} /** 从组合对象中移出某个组件对象 */
public void removeChild(Component child) {
// 缺省的实现,抛出例外,因为叶子对象没有这个功能,或者子组件没有实现这个功能
throw new UnsupportedOperationException("对象不支持这个功能");
} /** 返回某个索引对应的组件对象 */
public Component getChildren(int index) {
// 缺省的实现,抛出例外,因为叶子对象没有这个功能,或者子组件没有实现这个功能
throw new UnsupportedOperationException("对象不支持这个功能");
}
}

(2)接下来看看Composite对象的定义,示例代码如下:

/**
* 组合对象,通常需要存储子对象,定义有子部件的部件行为,
* 并实现在Component里面定义的与子部件有关的操作
*/
public class Composite extends Component {
/**
* 用来存储组合对象中包含的子组件对象
*/
private List<Component> childComponents = null; /** 示意方法,通常在里面需要实现递归的调用 */
public void someOperation() {
if (childComponents != null){
for(Component c : childComponents){
//递归的进行子组件相应方法的调用
c.someOperation();
}
}
}
public void addChild(Component child) {
//延迟初始化
if (childComponents == null) {
childComponents = new ArrayList<Component>();
}
childComponents.add(child);
} public void removeChild(Component child) {
if (childComponents != null) {
childComponents.remove(child);
}
} public Component getChildren(int index) {
if (childComponents != null){
if(index>=0 && index<childComponents.size()){
return childComponents.get(index);
}
}
return null;
}
}

(3)该来看叶子对象的定义了,相对而言比较简单,示例代码如下:

/**
* 叶子对象,叶子对象不再包含其它子对象
*/
public class Leaf extends Component {
/** 示意方法,叶子对象可能有自己的功能方法 */
public void someOperation() {
// do something
} }

(4)对于Client,就是使用Component接口来操作组合对象结构,由于使用方式千差万别,这里仅仅提供一个示范性质的使用,顺便当作测试代码使用,示例代码如下:

public class Client {
public static void main(String[] args) {
//定义多个Composite对象
Component root = new Composite();
Component c1 = new Composite();
Component c2 = new Composite();
//定义多个叶子对象
Component leaf1 = new Leaf();
Component leaf2 = new Leaf();
Component leaf3 = new Leaf(); //组和成为树形的对象结构
root.addChild(c1);
root.addChild(c2);
root.addChild(leaf1); c1.addChild(leaf2);
c2.addChild(leaf3); //操作Component对象
Component o = root.getChildren(1);
System.out.println(o);
}
}

二、父组件引用

(1) Component

public abstract class Component {
/**
* 记录父组件对象
*/
private Component parent = null; /**
* 获取一个组件的父组件对象
* @return 一个组件的父组件对象
*/
public Component getParent() {
return parent;
}
/**
* 设置一个组件的父组件对象
* @param parent 一个组件的父组件对象
*/
public void setParent(Component parent) {
this.parent = parent;
}
/**
* 返回某个组件的子组件对象
* @return 某个组件的子组件对象
*/
public List<Component> getChildren() {
throw new UnsupportedOperationException("对象不支持这个功能");
} /*-------------------以下是原有的定义----------------------*/ /**
* 输出组件自身的名称
*/
public abstract void printStruct(String preStr); /**
* 向组合对象中加入组件对象
* @param child 被加入组合对象中的组件对象
*/
public void addChild(Component child) {
// 缺省的实现,抛出例外,因为叶子对象没有这个功能,或者子组件没有实现这个功能
throw new UnsupportedOperationException("对象不支持这个功能");
} /**
* 从组合对象中移出某个组件对象
* @param child 被移出的组件对象
*/
public void removeChild(Component child) {
// 缺省的实现,抛出例外,因为叶子对象没有这个功能,或者子组件没有实现这个功能
throw new UnsupportedOperationException("对象不支持这个功能");
} /**
* 返回某个索引对应的组件对象
* @param index 需要获取的组件对象的索引,索引从0开始
* @return 索引对应的组件对象
*/
public Component getChildren(int index) {
throw new UnsupportedOperationException("对象不支持这个功能");
}
}

(2) Leaf

public class Leaf extends Component {
/**
* 叶子对象的名字
*/
private String name = ""; /**
* 构造方法,传入叶子对象的名字
* @param name 叶子对象的名字
*/
public Leaf(String name){
this.name = name;
} /**
* 输出叶子对象的结构,叶子对象没有子对象,也就是输出叶子对象的名字
* @param preStr 前缀,主要是按照层级拼接的空格,实现向后缩进
*/
public void printStruct(String preStr){
System.out.println(preStr+"-"+name);
}
}

(3) Composite

public class Composite extends Component{

    public void addChild(Component child) {
//延迟初始化
if (childComponents == null) {
childComponents = new ArrayList<Component>();
}
childComponents.add(child); //添加对父组件的引用
child.setParent(this);
} public void removeChild(Component child) {
if (childComponents != null) {
//查找到要删除的组件在集合中的索引位置
int idx = childComponents.indexOf(child);
if (idx != -1) {
//先把被删除的商品类别对象的父商品类别,设置成为被删除的商品类别的子类别的父商品类别
for(Component c : child.getChildren()){
//删除的组件对象是本实例的一个子组件对象
c.setParent(this);
//把被删除的商品类别对象的子组件对象添加到当前实例中
childComponents.add(c);
} //真的删除
childComponents.remove(idx);
}
}
} public List<Component> getChildren() {
return childComponents;
} /*-------------------以下是原有的实现,没有变化----------------------*/ /**
* 用来存储组合对象中包含的子组件对象
*/
private List<Component> childComponents = null; /**
* 组合对象的名字
*/
private String name = ""; /**
* 构造方法,传入组合对象的名字
* @param name 组合对象的名字
*/
public Composite(String name){
this.name = name;
} /**
* 输出组合对象自身的结构
* @param preStr 前缀,主要是按照层级拼接的空格,实现向后缩进
*/
public void printStruct(String preStr){
//先把自己输出去
System.out.println(preStr+"+"+this.name);
//如果还包含有子组件,那么就输出这些子组件对象
if(this.childComponents!=null){
//然后添加一个空格,表示向后缩进一个空格
preStr+=" ";
//输出当前对象的子对象了
for(Component c : childComponents){
//递归输出每个子对象
c.printStruct(preStr);
}
}
}
}

三、总结

(1) 组合模式的本质

组合模式的本质: 统一叶子对象和组合对象。

组合模式通过把叶子对象当成特殊的组合对象看待,从而对叶子对象和组合对象一视同仁,统统当成了Component对象,有机的统一了叶子对象和组合对象。

正是因为统一了叶子对象和组合对象,在将对象构建成树形结构的时候,才不需要做区分,反正是组件对象里面包含其它的组件对象,如此递归下去;也才使得对于树形结构的操作变得简单,不管对象类型,统一操作。

(2) 何时选用组合模式

  • 如果你想表示对象的部分-整体层次结构,可以选用组合模式,把整体和部分的操作统一起来,使得层次结构实现更简单,从外部来使用这个层次结构也简单。

  • 如果你希望统一的使用组合结构中的所有对象,可以选用组合模式,这正是组合模式提供的主要功能。

(3) 组合模式的优缺点

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

    在组合模式中,基本对象可以被组合成更复杂的组合对象,而组合对象又可以组合成更复杂的组合对象,可以不断地递归组合下去,从而构成一个统一的组合对象的类层次结构

  • 统一了组合对象和叶子对象

    在组合模式中,可以把叶子对象当作特殊的组合对象看待,为它们定义统一的父类,从而把组合对象和叶子对象的行为统一起来

  • 简化了客户端调用

    组合模式通过统一组合对象和叶子对象,使得客户端在使用它们的时候,就不需要再去区分它们,客户不关心使用的到底是什么类型的对象,这就大大简化了客户端的使用

  • 更容易扩展

    由于客户端是统一的面对Component来操作,因此,新定义的Composite或Leaf子类能够很容易的与已有的结构一起工作,而客户端不需要为增添了新的组件类而改变

  • 很难限制组合中的组件类型

    容易增加新的组件也会带来一些问题,比如很难限制组合中的组件类型。这在需要检测组件类型的时候,使得我们不能依靠编译期的类型约束来完成,必须在运行期间动态检测。


每天用心记录一点点。内容也许不重要,但习惯很重要!

Java 设计模式系列(九)组合模式的更多相关文章

  1. Java设计模式系列-抽象工厂模式

    原创文章,转载请标注出处:https://www.cnblogs.com/V1haoge/p/10755412.html 一.概述 抽象工厂模式是对工厂方法模式的再升级,但是二者面对的场景稍显差别. ...

  2. Java设计模式系列-工厂方法模式

    原创文章,转载请标注出处:<Java设计模式系列-工厂方法模式> 一.概述 工厂,就是生产产品的地方. 在Java设计模式中使用工厂的概念,那就是生成对象的地方了. 本来直接就能创建的对象 ...

  3. Java设计模式系列-装饰器模式

    原创文章,转载请标注出处:<Java设计模式系列-装饰器模式> 一.概述 装饰器模式作用是针对目标方法进行增强,提供新的功能或者额外的功能. 不同于适配器模式和桥接模式,装饰器模式涉及的是 ...

  4. 设计模式系列之组合模式(Composite Pattern)——树形结构的处理

    说明:设计模式系列文章是读刘伟所著<设计模式的艺术之道(软件开发人员内功修炼之道)>一书的阅读笔记.个人感觉这本书讲的不错,有兴趣推荐读一读.详细内容也可以看看此书作者的博客https:/ ...

  5. [js高手之路]设计模式系列课程-组合模式+寄生组合继承实战新闻列表

    所谓组合模式,就是把一堆结构分解出来,组成在一起,现实中很多这样的例子,如: 1.肯德基套餐就是一种组合模式, 比如鸡腿堡套餐,一般是是由一个鸡腿堡,一包薯条,一杯可乐等组成的 2.组装的台式机同理, ...

  6. JAVA设计模式初探之组合模式

    先看看组合模式的定义吧:“将对象组合成树形结构以表示‘部分-整体’的层次结构.组合模式使得用户对单个对象和组合对象的使用具有一致性.” 就拿剪发办卡的事情来分析一下吧. 首先,一张卡可以在总部,分店, ...

  7. Java设计模式之《组合模式》及应用场景

    摘要: 原创作品,可以转载,但是请标注出处地址http://www.cnblogs.com/V1haoge/p/6489827.html 组合模式,就是在一个对象中包含其他对象,这些被包含的对象可能是 ...

  8. C#设计模式之九组合模式(Composite Pattern)【结构型】

    一.引言 今天我们要讲[结构型]设计模式的第四个模式,该模式是[组合模式],英文名称是:Composite Pattern.当我们谈到这个模式的时候,有一个物件和这个模式很像,也符合这个模式要表达的意 ...

  9. Java设计模式学习记录-组合模式

    前言 今天要介绍的设计模式是组合模式,组合模式也是结构型设计模式的一种,它主要体现了整体与部分的关系,其典型的应用就是树形结构.组合是一组对象,其中的对象可能包含一个其他对象,也可能包含一组其他对象. ...

  10. java设计模式-----13、组合模式

    Composite模式也叫组合模式,是构造型的设计模式之一.通过递归手段来构造树形的对象结构,并可以通过一个对象来访问整个对象树. 组合(Composite)模式的其它翻译名称也很多,比如合成模式.树 ...

随机推荐

  1. UOJ22. 【UR #1】外星人【DP】【思维】

    LINK 题目大意 给你一个序列和一个值x 问你用某种方式对序列安排顺序之后一次对x取mod膜的最大值和方案数 首先发现一个性质 一个数之后所有比它大的数都没有贡献 考虑怎么利用这个性质? 就可以从小 ...

  2. Codeforces 106A:Card Game

    题目链接http://codeforces.com/contest/106/problem/A 题意:一套牌有S.H.D.C四种花色,按等级分成6.7.8.9.T.J.Q.K.A.每次选出一个花色作为 ...

  3. idea 使用spring boot 搭建freemarker模板

      一丶新建maven spring boot 项目 新建好了开始使用模板 先看一个目录结构 二丶配置pox.xml <?xml version="1.0" encoding ...

  4. linuxkit 基本试用

    安装 linux go get -u github.com/linuxkit/linuxkit/src/cmd/linuxkit mac brew tap linuxkit/linuxkit brew ...

  5. nsq 安装试用

    因为是mac 系统安装试用brew install nsq 安装 brew install nsq 组件说明 nsqd 守护进程进行消息的接受,缓存以及传递消息给客户端,需要配置nsqlookupd地 ...

  6. Swift学习——A Swift Tour 协议和扩展

    版权声明:本文为博主原创文章,未经博主同意不得转载. https://blog.csdn.net/zhenyu5211314/article/details/28854395 Protocols an ...

  7. sysstat工具

    sysstat工具可以监控系统的IO,CPU,SWAP,LOAD,NETWORK,DISK 安装后,系统会生成定时任务脚本 路径:/etc/cron.d/sysstat 内容: # Run syste ...

  8. eclipse启动报错:An error has occurred.See the log file D:\eclipse\configuration\1552616709202.log

    如题,Eclipse崩了,只能按它留下的线索去看了1552616709202.log: !SESSION -- ::08.739 ----------------------------------- ...

  9. SQL 相关分页方法

    [1] SET ANSI_NULLS ONGOSET QUOTED_IDENTIFIER OFFGO ALTER PROCEDURE [dbo].[procCom_Get_Pagination]( @ ...

  10. Mysql8.0 Public Key Retrieval is not allow错误的解决办法

    在使用Mysql 8.0时重启后启动项目的事后会报错com.mysql.jdbc.exceptions.jdbc4.MysqlNonTransientConnectionException: Publ ...