组合模式(Composite Pattern),又叫部分整体模式,是用于把一组相似的对象当作一个单一的对象。 组合模式依据树形结构来组合对象,用来表示部分以及整体层次。 这种类型的设计模式属于结构型模式,它创建了对象组的树形结构。
这种模式创建了一个包含自己对象组的类。该类提供了修改相同对象组的方式。
它是一种将对象组合成树状的层次结构的模式,用来表示“部分-整体”的关系,使用户对单个对象和组合对象具有一致的访问性。
这个模式在我们的生活中也经常使用,比如说如果读者有使用Java的GUI编写过程序的, 肯定少不了定义一些组件,初始化之后,然后使用容器的add方法,将这些组件有顺序的组织成一个界面出来; 或者读者如果编写过前端的页面,肯定使用过
等标签定义一些格式,然后格式之间互相组合, 通过一种递归的方式组织成相应的结构,这种方式其实就是组合,将部分的组件镶嵌到整体之中; 又或者文件和文件夹的组织关系,通过目录表项作为共同的特质(父类),一个文件夹可以包含多个文件夹和多个文件, 一个文件容纳在一个文件夹之中。那么凭什么可以这样做呢,需要满足以下两点,
- 首先整体的结构应该是一棵树,
- 第二,所有的组件应该有一个共同的父类(有共同的本质),这个父类使得组件中的共同的本质可以提取出来 (有了共同语言(父类)),进行互融,其实就是父类使用add方法,这样子类就可以通过抽象的方式通过父类来表达了, 可能有点绕口
介绍
- 意图:将对象组合成树形结构以表示"部分-整体"的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。
- 主要解决:它在我们树型结构的问题中,模糊了简单元素和复杂元素的概念,客户程序可以向处理简单元素一样来处理复杂元素,从而使得客户程序与复杂元素的内部结构解耦。
- 何时使用:
- 您想表示对象的部分-整体层次结构(树形结构)。
- 您希望用户忽略组合对象与单个对象的不同,用户将统一地使用组合结构中的所有对象。
- 如何解决:树枝和叶子实现统一接口,树枝内部组合该接口。
- 关键代码:树枝内部组合该接口,并且含有内部属性 List,里面放 Component。
应用实例:
- 算术表达式包括操作数、操作符和另一个操作数,其中,另一个操作符也可以是操作数、操作符和另一个操作数。
- 在 JAVA AWT 和 SWING 中,对于 Button 和 Checkbox 是树叶,Container 是树枝。
优点
- 高层模块调用简单。
- 节点自由增加。
组合模式使得客户端代码可以一致地处理单个对象和组合对象,无须关心自己处理的是单个对象,还是组合对象,这简化了客户端代码; 更容易在组合体内加入新的对象,客户端不会因为加入了新的对象而更改源代码,满足“开闭原则”;
缺点
在使用组合模式时,其叶子和树枝的声明都是实现类,而不是接口,违反了依赖倒置原则。
设计较复杂,客户端需要花更多时间理清类之间的层次关系;不容易限制容器中的构件;不容易用继承的方法来增加构件的新功能;
使用场景
部分、整体场景,如树形菜单,文件、文件夹的管理。
在需要表示一个对象整体与部分的层次结构的场合。 要求对用户隐藏组合对象与单个对象的不同,用户可以用统一的接口使用组合结构中的所有对象的场合。
注意事项
定义时为具体类。
代码展示
示例一
package designpatterns.composite.shopping; //抽象构件:物品 public interface Articles { public float calculation(); //计算 public void show(); }
package designpatterns.composite.shopping; import java.util.ArrayList; //树枝构件:袋子 public class Bags implements Articles { private String name; //名字 private ArrayList<Articles> bags = new ArrayList<Articles>(); public Bags(String name) { this.name = name; } public void add(Articles c) { bags.add(c); } public void remove(Articles c) { bags.remove(c); } public Articles getChild(int i) { return bags.get(i); } public float calculation() { float s = 0; for (Object obj : bags) { s += ((Articles) obj).calculation(); } return s; } public void show() { for (Object obj : bags) { ((Articles) obj).show(); } } }
package designpatterns.composite.shopping; //树叶构件:商品 public class Goods implements Articles { private String name; //名字 private int quantity; //数量 private float unitPrice; //单价 public Goods(String name, int quantity, float unitPrice) { this.name = name; this.quantity = quantity; this.unitPrice = unitPrice; } public float calculation() { return quantity * unitPrice; } public void show() { System.out.println(name + "(数量:" + quantity + ",单价:" + unitPrice + "元)"); } }
package designpatterns.composite.shopping; public class ShoppingTest { public static void main(String[] args) { float s = 0; Bags BigBag, mediumBag, smallRedBag, smallWhiteBag; Goods sp; BigBag = new Bags("大袋子"); mediumBag = new Bags("中袋子"); smallRedBag = new Bags("红色小袋子"); smallWhiteBag = new Bags("白色小袋子"); sp = new Goods("婺源特产", 2, 7.9f); smallRedBag.add(sp); sp = new Goods("婺源地图", 1, 9.9f); smallRedBag.add(sp); sp = new Goods("韶关香菇", 2, 68); smallWhiteBag.add(sp); sp = new Goods("韶关红茶", 3, 180); smallWhiteBag.add(sp); sp = new Goods("景德镇瓷器", 1, 380); mediumBag.add(sp); mediumBag.add(smallRedBag); sp = new Goods("李宁牌运动鞋", 1, 198); BigBag.add(sp); BigBag.add(smallWhiteBag); BigBag.add(mediumBag); System.out.println("您选购的商品有:"); BigBag.show(); s = BigBag.calculation(); System.out.println("要支付的总价是:" + s + "元"); } }
示例二
package designpatterns.composite.zyr; /** * Entry 抽象类:共同特质 */ public abstract class Entry { public abstract String getName(); public abstract int getSize(); public abstract void printList(String prefix); public void printList() { printList(""); } public Entry add(Entry entry) throws RuntimeException { throw new RuntimeException(); } public String toString() { return getName() + "<" + getSize() + ">"; } }
package designpatterns.composite.zyr; /** * File 类:实现类,叶子结点 */ public class File extends Entry { private String name; private int size; public File(String name, int size) { this.name = name; this.size = size; } public String getName() { return name; } public int getSize() { return size; } public void printList(String prefix) { System.out.println(prefix + "/" + this); } }
package designpatterns.composite.zyr; import java.util.ArrayList; import java.util.Iterator; public class Directory extends Entry { String name; ArrayList entrys = new ArrayList(); public Directory(String name) { this.name = name; } public String getName() { return name; } public int getSize() { int size = 0; Iterator it = entrys.iterator(); while (it.hasNext()) { size += ((Entry) it.next()).getSize(); } return size; } public Entry add(Entry entry) { entrys.add(entry); return this; } public void printList(String prefix) { System.out.println(prefix + "/" + this); Iterator it = entrys.iterator(); Entry entry; while (it.hasNext()) { entry = (Entry) it.next(); entry.printList(prefix + "/" + name); } } }
package designpatterns.composite.zyr; public class CompositeZYRMain { public static void main(String[] args) { Directory life = new Directory("我的生活"); File eat = new File("吃火锅", 100); File sleep = new File("睡觉", 100); File study = new File("学习", 100); life.add(eat); life.add(sleep); life.add(study); Directory work = new Directory("我的工作"); File write = new File("写博客", 200); File paper = new File("写论文", 200); File homework = new File("写家庭作业", 200); work.add(write); work.add(paper); work.add(homework); Directory relax = new Directory("我的休闲"); File music = new File("听听音乐", 200); File walk = new File("出去转转", 200); relax.add(music); relax.add(walk); Directory read = new Directory("我的阅读"); File book = new File("学习书籍", 200); File novel = new File("娱乐小说", 200); read.add(book); read.add(novel); Directory root = new Directory("根目录"); root.add(life); root.add(work); root.add(relax); root.add(read); root.printList("D:"); System.out.println("================="); work.printList("work"); System.out.println("================="); novel.printList("novel"); } }