【原创·教程·连载】《Android之大话设计模式》--设计模式之结构型模式 第十三章:组合模式 MM的生日礼物
<大话设计模式>
本教程说明及版权声明
国士工作室是一支专注于Android平台企业级应用开发的技术团队,致力于做中国最棒的Android应用程序开发机构,提供最棒的Android企业级应用开发培训服务。
企业培训和开发合作官方联系方式:
电话:18610086859
Email:hiheartfirst@gmail.com
QQ:1740415547
QQ群:148325348
国士工作室 有你更美好!
l 该文档参考和使用了网络上的免费开放的图片和内容,并以免费开放的方式发布,希望为移动互联网和智能手机时代贡献绵薄之力!可以随意转载,但不得使用该文档谋利。
l 如果对该文档有任何疑问或者建议,请进入官方博客
http://www.cnblogs.com/guoshiandroid/留言或者直接与国士工作室联系(后附联系方式),我们会慎重参考您的建议并根据需要对本文档进行修改,以造福更多开发者!
l 《大话设计模式》的最新及完整内容会在国士工作室官方博客定期更新,请访问国士工作室博客
http://www.cnblogs.com/guoshiandroid/获取更多更新内容。
组合模式 MM的生日礼物
组合模式应用场景举例:
MM今天过生日。对GG说道“今天我过生日,你要送我一件大礼物:-O“,“嗯,好吧,去商店,你自己挑吧。”“这件T恤挺漂亮,再加上这条裙子,哎,再来一个这个包也不错。就是它了,一套大礼。”,“能不能在包里面再装点什么,算是送我一个满的包……”,GG无语~~o(>_<)o ~~
组合模式解释:
组合模式(Composite Pattern)是构造型的设计模式之一,是指将对象组合成树形结构以表示“部分-整体”的层次结构,组合模式使得客户对单个对象和组合对象的使用具有一致性。
英文定义为:Compose objects into tree
structures to represent part-whole hierarchies. Composite lets clients treat
individual objects and compositions of objects uniformly.
组合模式的UML图:
组合模式所涉及的角色如下:
抽象组件角色(Component):它为组合中的对象声明接口,也可以为共有接口实现缺省行为。
树叶组件角色(Leaf):在组合中表示叶节点对象没有子节点,实现抽象组件角色声明的接口。
树枝组件角色(Composite):在组合中表示分支节点对象,有子节点,实现抽象组件角色声明的接口;存储子部件。
组合模式的UML图如下 所示:
组合模式深入分析:
将客户代码与复杂的对象容器结构解耦是组合模式的核心思想,解耦之后,客户代码将与纯粹的抽象接口——而非对象容器的复内部实现结构——发生依赖关系,从而更能应对变化。
组合模式中必须提供对子对象的管理方法,不然无法完成对子对象的添加删除等等操作,也就失去了灵活性和扩展性。但是管理方法是在Component中就声明还是在Composite中声明呢?一种方式是在Component里面声明所有的用来管理子类对象的方法,以达到Component接口的最大化。目的就是为了使客户看来在接口层次上树叶和分支没有区别——透明性。但树叶是不存在子类的,因此声明的一些方法对于树叶来说是不适用的。这样也就带来了一些安全性问题。另一种方式就是只在Composite里面声明所有的用来管理子类对象的方法。这样就避免了上一种方式的安全性问题,但是由于叶子和分支有不同的接口,所以又失去了透明性。《设计模式》一书认为:在这一模式中,相对于安全性,我们比较强调透明性。对于第一种方式中叶子节点内不需要的方法可以使用空处理或者异常报告的方式来解决。
组合模式在具体实现中,可以让父对象中的子对象反向追溯;如果父对象有频繁的遍历需求,可使用缓存技巧来改善效率。
组合模式使用场景分析及代码实现:
在上面的使用场景中,MM的生日礼物可以看做根节点,一套大礼物可以看做是树枝构组件角色,一套大礼物中的T恤、裙子和包包是树叶组件角色,UML图如下所示:
建立礼物节点的抽象接口:
package com.diermeng.designPattern.Composite; import java.util.List;
/* * 礼物节点的抽象 */ public interface Gift {
//显示礼物的树枝角色或者树叶角色的名称 public void display();
//添加 public boolean add(Gift file);
//移除 public boolean remove(Gift file);
//获得子节点 public List<Gift> getChildren(); }
|
礼物的树枝节点:
package com.diermeng.designPattern.Composite.impl; import java.util.ArrayList; import java.util.List;
import com.diermeng.designPattern.Composite.Gift;
/* * 礼物的树枝节点 实现了礼物的抽象节点 */ public class GiftComposite implements Gift{ /* * 礼物树枝节点的名称属性 */ private String name; /* * 礼物树枝节点的子节点 */ private List<Gift> children;
public GiftComposite(String name) { this.name = name; children = new ArrayList<Gift>(); }
public void display() { System.out.println(name); }
public List<Gift> getChildren() { return children; }
public boolean add(Gift file) { return children.add(file); }
public boolean remove(Gift file) { return children.remove(file); }
}
|
建立礼物节点的树叶节点:
package com.diermeng.designPattern.Composite.impl; import java.util.List;
import com.diermeng.designPattern.Composite.Gift;
public class GiftLeaf implements Gift { private String name;
public GiftLeaf(String name) { this.name = name; }
public void display() { System.out.println(name); }
public List<Gift> getChildren() { return null; }
public boolean add(Gift file) { return false; }
public boolean remove(Gift file) { return false; }
}
|
建立一个测试客户端:
package com.diermeng.designPattern.Composite.client; import java.util.List;
import com.diermeng.designPattern.Composite.Gift; import com.diermeng.designPattern.Composite.impl.GiftComposite; import com.diermeng.designPattern.Composite.impl.GiftLeaf;
public class CompositeTest { public static void main(String[] args) { //树枝组件节点 Gift gift = new GiftComposite("大礼物"); //树叶组件节点 Gift shirt = new GiftLeaf("T恤"); Gift skirt = new GiftLeaf("裙子"); Gift bag = new GiftLeaf("包包");
//把树叶节点加入到树枝节点中 gift.add(shirt); gift.add(skirt); gift.add(bag);
//调用树的遍历方法,来显示整棵树 displayTree(gift,0);
}
public static void displayTree(Gift gift, int deep) { for(int i = 0; i < deep; i++) { System.out.print("--"); } //显示自身的名称 gift.display(); //获得子树 List<Gift> children = gift.getChildren(); //遍历子树 for(Gift file : children) { if(file instanceof GiftLeaf) { for(int i = 0; i <= deep; i++) { System.out.print("--"); } file.display(); } else { displayTree(file,deep + 1); } } } } |
运行结果如下:
大礼物 --T恤 --裙子 --包包 |
组合模式的优缺点分析:
优点:
使客户端调用简单,客户端可以一致的使用组合结构或其中单个对象,用户就不必关系自己处理的是单个对象还是整个组合结构,这就简化了客户端代码。
更容易在组合体内加入对象组件.
客户端不必因为加入了新的对象组件而更改代码。
缺点:
使用组合模式虽然可以更容易的在组合体内加入新的对象组件,带来了很大的灵活性,而且客户端也用户为此修改代码,但是,如果不对新的对象组件加以合理的控制,会构成非常庞大的树形结构,这就会导致在遍历的时候过大的内存开销。
组合模式的实际应用简介:
组合模式适用于以下的情况:
第一:用于表示部分-整体结构
第二:希望客户端忽略组合对象恩和单个对象,客户端将统一的使用组合结构中的所有对象。
同时在使用组合模式的有以下要点:
组合模式采用树形结构来实现普遍存在的对象容器,从而将一对多的关系转化一对一的关系,使得客户代码可以一致地处理对象和对象容器,无需关心处理的是单个的对象,还是组合的对象容器。
将客户代码与复杂的对象容器结构解耦是组合模式的核心思想,解耦之后,客户代码将与纯粹的抽象接口——而非对象容器的复内部实现结构——发生依赖关系,从而更能应对变化。
组合模式中,是将Add和Remove等和对象容器相关的方法定义在;表示抽象对象的Component类中,还是将其定义在表示对象容器的中,是一个关乎透明性和安全性的两难问题,需要仔细权衡。建议才有透明的方式,这里有可能违背面向对象的单一职责原则,但是对于这种特殊结构,这又是必须付出的代价。
组合模式在具体实现中,可以让父对象中的子对象反向追溯;如果父对象有频繁的遍历需求,可使用缓存技巧来改善效率。
温馨提示:
组合模式解耦了客户端程序和复杂元素内部结构,从而使客户端程序可以像处理简单元素一样来处理复杂元素。
在整个对象树中,如果我们经常从子对象获得父对象,尤其是活的根节点的对象,我们可以采用缓存技巧来改善效率。