设计模式之组合模式(Composite)
1.引言
在学习JUnit的时候,看到一段话“JUnit框架是一个典型的Composite模式:TestSuite可以容纳任何派生自Test的对象;当调用TestSuite对象的run()方法是,会遍历自己容纳的对象,逐个调用它们的run()方法”。就来学习什么是组合模式。
2.应用实例
在实现跟商品有关的应用系统的时候,一个很常见的功能就是商品类别树的管理,比如有以下的商品类别树:
——————————————————————————————————
-服装
-男装
-衬衣
-夹克
-女装
-裙子
-套装
——————————————————————————————————
通过上面的商品类别树我们可以发现商品类别树有两种类型的节点,分别是叶子节点(衬衣,夹克)和组合节点(服装,男装,女装)。组合节点中可以包含其他的组合节点或者叶子节点,而叶子节点不能。
给出一个简单的管理商品类别树的实例代码。
2.1不使用组合模式的解决方案
组合节点Composite
package edu.sjtu.erplab.designpattern.composite.exp1; import java.util.ArrayList;import java.util.Collection; public class Composite {private Collection<Composite> childComposite=new ArrayList<Composite>();private Collection<Leaf> childLeaf=new ArrayList<Leaf>();private String name="";public Composite(String name) {this.name=name; }//向组合对象中加入其他组合对象 public void addComposite(Composite c) {this.childComposite.add(c); }//向组合对象加入其他叶子对象 public void addLeaf(Leaf l) {this.childLeaf.add(l); } //输出组合对象结构 public void printStruct(String preStr) { System.out.println(preStr+"-"+name); preStr+=" ";for(Leaf leaf:childLeaf) { leaf.printStruct(preStr); }for(Composite composite:childComposite) { composite.printStruct(preStr); } }}
叶子节点
package edu.sjtu.erplab.designpattern.composite.exp1; public class Leaf {private String name="";public Leaf(String name){this.name=name; }public void printStruct(String preStr) { System.out.println(preStr+"-"+name); } }
客户端Client
package edu.sjtu.erplab.designpattern.composite.exp1; public class Client {public static void main(String args[]) {//定义组合对象 Composite root=new Composite("服装"); Composite c1=new Composite("男装"); Composite c2=new Composite("女装"); //定义叶子对象 Leaf l1=new Leaf("衬衣"); Leaf l2=new Leaf("夹克"); Leaf l3=new Leaf("裙子"); Leaf l4=new Leaf("套装"); //按照树的结构来组合对象 root.addComposite(c1); root.addComposite(c2); c1.addLeaf(l1); c1.addLeaf(l2); c2.addLeaf(l3); c2.addLeaf(l4); root.printStruct(""); } }
运行结果就是应用实例中显示的商品树形结构。
上述解决方案存在的问题:
虽然实现了要求的功能,但是有一个明显的问题:那就是必须区分组合对象和叶子对象,并进行区别对待,比如在Composite(addComposite方法和addLeaf方法)和Client(定义Composite对象和定义Leaf对象)里面,都需要去区别对待这两种对象。区别对待组合对象与叶子对象不但让程序更加复杂,还对功能的扩展带了了不便。用户不希望区别对待这两类对象。
2.2使用组合模式的解决方案
组合模式通过引入一个抽象的组件对象,作为组合对象和叶子对象的父对象,这样就把组合对象和叶子对象统一起来了,用户使用的时候,始终是在操作组件对象,而不再去区分是在操作组合对象还是叶子对象。
组合模式的关键就在于这个抽象类,这个抽象类既可以代表叶子对象,也可以代表组合对象,这样用户在操作的时候,对叶子对象和组合对象的使用就具有了一致性。
组件对象Component
package edu.sjtu.erplab.designpattern.composite.exp2; public abstract class Component { public abstract void printStruct(String preStr); //透明性 public void addChild(Component child){throw new UnsupportedOperationException("对象不支持这个功能"); } public void removeChild(Component child){throw new UnsupportedOperationException("对象不支持这个功能"); } public Component getChild(Component child){throw new UnsupportedOperationException("对象不支持这个功能"); } }
组合对象Composite
package edu.sjtu.erplab.designpattern.composite.exp2; import java.util.ArrayList;import java.util.Collection;import java.util.List; public class Composite extends Component { private List<Component> childComponents = null;private String name = ""; public Composite(String name) {this.name=name; } @Overridepublic void addChild(Component child) {// TODO Auto-generated method stub if (childComponents == null) { childComponents = new ArrayList<Component>(); } childComponents.add(child); } @Overridepublic void removeChild(Component child) {// TODO Auto-generated method stub super.removeChild(child); } @Overridepublic Component getChild(Component child) {// TODO Auto-generated method stub return super.getChild(child); } public void printStruct(String preStr) { System.out.println(preStr + "-" + name);if (this.childComponents != null) { preStr += " ";for (Component c : childComponents) { c.printStruct(preStr); } } } }
叶子对象Leaf
package edu.sjtu.erplab.designpattern.composite.exp2; public class Leaf extends Component {private String name="";public Leaf(String name){this.name=name; } //必须实现父类的抽象方法 public void printStruct(String preStr) { System.out.println(preStr+"-"+name); } }
客户端
package edu.sjtu.erplab.designpattern.composite.exp2; public class Client {public static void main(String args[]) {//定义组合对象 Component root=new Composite("服装"); Component c1=new Composite("男装"); Component c2=new Composite("女装"); //定义叶子对象 Component l1=new Leaf("衬衣"); Component l2=new Leaf("夹克"); Component l3=new Leaf("裙子"); Component l4=new Leaf("套装"); //按照树的结构来组合对象 root.addChild(c1); root.addChild(c2); c1.addChild(l1); c1.addChild(l2); c2.addChild(l3); c2.addChild(l4); root.printStruct(""); } }
程序架构如下图所示。这样的架构实现了用户的透明访问。
3.透明性与安全性的权衡考虑
如上图所示,在Component组件中定义了操作组合节点的方法addChild,removeChild等,这些方法被Leaf继承,因此Leaf也能够调用,但是叶子节点是不能进行增加子节点和删除子节点的。这样就存在安全性的问题。
基于安全性考虑的组合模式的解决方案
组件节点Component
package edu.sjtu.erplab.designpattern.composite.exp3; public abstract class Component { public abstract void printStruct(String preStr); }
组合节点Composite和叶子节点Leaf没有改变
客户端发生改变,需要区分组合节点和叶子节点,这个跟第一个代码实例中类似。
package edu.sjtu.erplab.designpattern.composite.exp3; public class Client {public static void main(String args[]) {//定义组合对象 Composite root=new Composite("服装"); Composite c1=new Composite("男装"); Composite c2=new Composite("女装"); //定义叶子对象 Leaf l1=new Leaf("衬衣"); Leaf l2=new Leaf("夹克"); Leaf l3=new Leaf("裙子"); Leaf l4=new Leaf("套装"); //按照树的结构来组合对象 root.addChild(c1); root.addChild(c2); c1.addChild(l1); c1.addChild(l2); c2.addChild(l3); c2.addChild(l4); root.printStruct(""); } }
上述实例的结构如下图所示:
透明性组合模式与安全性组合模式的选择:
对于组合模式,在安全性和透明性上,会更加看重透明性,毕竟组合模式的功能就是让客户端对叶子对象和组合对象的使用具有一致性。
4.父组件引用
在上面的示例中,都是在父组件对象中,保存有子组件的引用,也就是说都是从父到子的引用,本节讨论一下子组件对象到父组件对象的引用,在实际开发过程中也非常有用,比如可以实现如下功能:
- 删除某个商品的类别, 如果是子对象,那么直接删除,如果是组合对象,那么将组合对象下的所有子对象的层级提高一层。
- 调整商品类别。
要实现上述功能, 一个较为简单的方案就是在保持从父组件到子组件引用的基础上,再增加保持从子组件到父组件的引用,这样在删除一个组件对象或者是调整一个组件对象的时候,可以通过调整父组件的引用来实现,可以大大简化实现。
实例说明如下:
组件对象Component
package edu.sjtu.erplab.designpattern.composite.exp4; import java.util.List; public abstract class Component { private Component parent=null; //获取某个组件的所有子对象 public List<Component> getChildren() {throw new UnsupportedOperationException("对象不支持这个功能"); }//获取父对象 public Component getParent() {return parent; } public void setParent(Component parent) {this.parent = parent; } public abstract void printStruct(String preStr); public void addChild(Component child){throw new UnsupportedOperationException("对象不支持这个功能"); } public void removeChild(Component child){throw new UnsupportedOperationException("对象不支持这个功能"); } public Component getChild(Component child){throw new UnsupportedOperationException("对象不支持这个功能"); } }
组合对象Composite
package edu.sjtu.erplab.designpattern.composite.exp4; import java.util.ArrayList;import java.util.Collection;import java.util.List; public class Composite extends Component { private List<Component> childComponents = null;private String name = ""; public Composite(String name) {this.name=name; } public void printStruct(String preStr) { System.out.println(preStr + "-" + name);if (this.childComponents != null) { preStr += " ";for (Component c : childComponents) { c.printStruct(preStr); } } } /** * 添加子组件 * child为具体的子组件 * 为child设置父组件。setParent*/ @Overridepublic void addChild(Component child) {if (childComponents == null) { childComponents = new ArrayList<Component>(); } childComponents.add(child);//添加对父组件的引用 child.setParent(this); } /** * 删除子组件*/ @Overridepublic 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); } } } @Overridepublic List<Component> getChildren() {return childComponents; }}
叶子节点没有变化,客户端有所改变
package edu.sjtu.erplab.designpattern.composite.exp4; public class Client {public static void main(String args[]) {//定义组合对象 Component root=new Composite("服装"); Component c1=new Composite("男装"); Component c2=new Composite("女装"); //定义叶子对象 Component l1=new Leaf("衬衣"); Component l2=new Leaf("夹克"); Component l3=new Leaf("裙子"); Component l4=new Leaf("套装"); //按照树的结构来组合对象 root.addChild(c1); root.addChild(c2); c1.addChild(l1); c1.addChild(l2); c2.addChild(l3); c2.addChild(l4); root.printStruct(""); System.out.println("---------------------------------"); root.removeChild(c1); root.printStruct(""); } }
程序运行结果:
——————————————————————————
-服装
-男装
-衬衣
-夹克
-女装
-裙子
-套装
---------------------------------
-服装
-女装
-裙子
-套装
-衬衣
-夹克
——————————————————————
程序架构结构图如下图所示:
5组合模式的优缺点及适用环境
优点:
- 定义了包含基本对象和组合对象的层次结构
- 统一了组合对象和叶子对象
- 简化了客户端调用
- 更容易扩展
缺点:
- 很难闲置组合中的组件类型
适用环境
- 如果你想表示对象的部分-整体层次结构,可以选用组合模式,把整体和部分的操作统一起来,是的层次结构实现更简单,从外部来使用这个层次结构也更加容易。
- 如果你希望统一得使用组合结构中的所有对象,可以选用组合模式,这正是组合模式提供的主要功能。
转载于:https://www.cnblogs.com/xwdreamer/archive/2012/03/29/2424034.html
设计模式之组合模式(Composite)相关推荐
- 【设计模式】组合模式 Composite Pattern
树形结构是软件行业很常见的一种结构,几乎随处可见, 比如: HTML 页面中的DOM,产品的分类,通常一些应用或网站的菜单,Windows Form 中的控件继承关系,Android中的View继承 ...
- 设计模式之组合模式(Composite)摘录
23种GOF设计模式一般分为三大类:创建型模式.结构型模式.行为模式. 创建型模式抽象了实例化过程,它们帮助一个系统独立于如何创建.组合和表示它的那些对象.一个类创建型模式使用继承改变被实例化的类,而 ...
- [设计模式] 8 组合模式 Composite
DP书上给出的定义:将对象组合成树形结构以表示"部分-整体"的层次结构.组合使得用户对单个对象和组合对象的使用具有一致性.注意两个字"树形".这种树形结构在现实 ...
- python 设计模式之组合模式Composite Pattern
#引入一 文件夹对我们来说很熟悉,文件夹里面可以包含文件夹,也可以包含文件. 那么文件夹是个容器,文件夹里面的文件夹也是个容器,文件夹里面的文件是对象. 这是一个树形结构 咱们生活工作中常用的一种结构 ...
- 研磨设计模式 之 组合模式(Composite) 3——跟着cc学设计系列
15.3 模式讲解 15.3.1 认识组合模式 (1)组合模式的目的 组合模式的目的是:让客户端不再区分操作的是组合对象还是叶子对象,而是以一个统一的方式来操作. 实现这个目标的关键之处,是设计一 ...
- 研磨设计模式 之 组合模式(Composite) 2——跟着cc学设计系列
15.2 解决方案 15.2.1 组合模式来解决 用来解决上述问题的一个合理的解决方案就是组合模式.那么什么是组合模式呢? (1)组合模式定义 (2)应用组合模式来解决的思路 仔细分析上面不用模式 ...
- 【设计模式】组合模式(Composite)
引入 商品类别树的节点被分成两种,一种是容器节点,另一种是叶子节点. 容器节点可以包含其他容器节点或者叶子节点 组合模式 组合模式有时又叫做部分--整体模式(Part-Whole) .组合模式将对象组 ...
- Java设计模式 —— 组合模式(Composite)
Java设计模式 -- 组合模式(Composite) 定义 Composite,组合模式:将对象组合成树形结构以表示部分整体的关系,Composite使得用户对单个对象和组合对象的使用具有一致性. ...
- 设计模式(17):结构型-组合模式(Composite)(2)
设计模式(Design pattern) 是一套被反复使用.多数人知晓的.经过分类编目的.代码设计经验的总结.使用设计模式是为了可重用代码.让代码更容易被他人理解.保证代码可靠性. 毫无疑问,设计模式 ...
最新文章
- 在Eclipse中写第一个hibernate小例子
- 打开VMware的系统出错
- [NOIP1999] 提高组 洛谷P1014 Cantor表
- Java – 2012年回顾和未来预测
- Linux学习总结(77)—— Shell 开发运维经验总结
- Android 蓝牙技术 实现终端间数据传输
- php数组比对算法,php学习之两种方法使用比较数组差异的array_diff()函数
- java地铁最短,地铁最短路径
- [转载] python set集合如何有序输出_python set集合的用法
- Javascript:Ajax案例实操
- 使用python编写聊天小程序
- mysql 存储过程 对象_【MYSQL笔记3】MYSQL过程式数据库对象之存储过程的调用、删除和修改...
- vc707 MicroBlaze LCD 试验
- c#(WinForm)绘制两个圆的内公切线
- 如何解决织梦后台上传一篇文章前端展示两篇完全一样的
- (九)苏世民:我的经验和教训:苏世民成功投资的十五条法则
- 时态的重建--适合理工直男的钟平老师逻辑英语学习笔记
- 一张图看懂阿里云ACK
- 深入理解Image.createImage()
- window批量合并txt文档
热门文章
- push declined due to email privacy restrictions
- 深入理解计算机系统 第三部分程序间的交互和通信
- Cocos2d-x中常用宏的作用
- 这4种分析方法,大牛产品经理都在用
- 为什么大厂都在造车?原因找到了
- 计算机组成原理补充实验,计算机组成原理实验补充实验指导-实.doc
- php5.6 xdebug 配置,php5.5+apache2.4+mysql5.6+xdebug配置
- mysql增错误_使用MySQL练习增删改查时因为版本问题出现连接错误
- 15 张图, 把TCP/IP 讲得一清二楚!
- 看完这篇Redis缓存三大问题,够你和面试官battle几回合了