组合(Composite)模式
组合模式(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");
}
}