将对象组合成树形结构来表现出“总体/部分”的层次结构。组合能让客户以一致性的方式处理个别的对象以及对象组合。windows
抽象组件(Component): 为组合中的对象(节点或者组件)声明接口,也可提供默认的接口缺省实现;api
节点对象(Leaf): 组合中的叶节点对象,叶节点对象再也不拥有子节点;安全
复合对象(Composite):容许存在多个子部件,须要包含的子部件,并实现对子部件的操做接口;bash
客户端(Client): 调用端,经过调用Component相关接口,操做组合中的对象;微信
透明模式:叶节点和组合对象所拥有的操做都放抽象组件Component,这样客户端调用时,不须要判断节点类型均可以进行api调用,无需类型转换。可是对应的存在安全性的问题,由于叶节点自己并不具有组合对象add、remove、get等等有关子节点的操做api,这样的设计可能致使调用后出现与预期不符的现象,由于客户有可能会作一些无心义 的事情,例如在Leaf 中增长和删除对象等。框架
安全模式:将组合对象独有的api操做都放在对应的实现类中,好处就是安全性提高,只有组合对象才会提供add、remove、get等操做。缺点就是不够透明,整个树形结构元素使用上,由于Leaf 和Composite具备不一样的接口,客户还得区分判断是叶节点或者组合对象,并进行类型转换才能够调用相应的api;ide
2种方式在于透明性和安全性的互换,这须要在安全性和透明性之间作出权衡选择,在这一模式中,相对于安全性,咱们比较强调透明性。若是你选择了安全性,有时你可能会丢失类型信息,而且不得不将一个组件转换成一个组合。这样的类型转换一定不是类型安全的。post
这边以透明模式为例:ui
将默认实现为抛出异常(也可空实现之类的,具体状况具体分析),子类根据须要进行重写this
public abstract class Component {
public abstract void operation();
public void add(Component component) {
throw new UnsupportedOperationException();
}
public void remove(Component component) {
throw new UnsupportedOperationException();
}
public Component get(int index) {
throw new UnsupportedOperationException();
}
}
复制代码
public class Leaf extends Component{
@Override
public void operation() {
//...
System.out.println("叶节点自身的操做");
}
}
复制代码
public class Composite extends Component{
List<Component> components = new ArrayList<Component>();
@Override
public void add(Component component) {
components.add(component);
}
@Override
public void remove(Component component) {
components.remove(component);
}
@Override
public Component get(int index) {
return components.get(index);
}
@Override
public void operation() {
for (Component component : components) {
component.operation();
}
}
}
复制代码
//建立过程通常是对调用端隐藏,Client不须要关系是建立的什么对象
Component component = new Composite();
Component leaf1 = new Leaf();
Component leaf2 = new Leaf();
component.add(leaf1);
List<Component> list = new ArrayList<>();
list.add(component);
list.add(leaf2);
//Client只要执行本身所须要的操做就行
for (Component component : list) {
component.operation();
}
复制代码
熟悉的windows文件目录结构就能够看出是组合模式中的树状图。节点能够是文件(Leaf),也能够是目录(Compostite),能够定义出共同的抽象组件(Component)提供接口: open、delete等相关文件操做。
public abstract class AbstractFile {
String fileName;
public AbstractFile(String fileName) {
this.fileName = fileName;
}
public abstract void open();
public abstract void delete();
public abstract boolean isDirect();
public void add(AbstractFile abstractFile) {
throw new UnsupportedOperationException();
}
public void remove(AbstractFile abstractFile) {
throw new UnsupportedOperationException();
}
public AbstractFile get(int index) {
throw new UnsupportedOperationException();
}
}
复制代码
public class File extends AbstractFile{
public File(String fileName) {
super(fileName);
}
@Override
public void open() {
System.out.println("打开文件" + fileName);
}
@Override
public void delete() {
System.out.println("删除文件" + fileName);
}
@Override
public boolean isDirect() {
return false;
}
}
复制代码
public class Directory extends AbstractFile{
List<AbstractFile> files = new ArrayList<>();
public Directory(String fileName) {
super(fileName);
}
@Override
public void add(AbstractFile abstractFile) {
files.add(abstractFile);
}
@Override
public void remove(AbstractFile abstractFile) {
files.remove(abstractFile);
}
@Override
public AbstractFile get(int index) {
return files.get(index);
}
@Override
public void open() {
for (AbstractFile abstractFile : files) {
abstractFile.open();
}
}
@Override
public void delete() {
for (AbstractFile abstractFile : files) {
abstractFile.delete();
}
}
@Override
public boolean isDirect() {
return true;
}
}
复制代码
假设进行文件删除:
AbstractFile directory = new Directory("目录");
AbstractFile file1 = new File("文件1");
AbstractFile file2 = new File("文件2");
directory.add(file1);
List<AbstractFile> list = new ArrayList<>();
list.add(directory);
list.add(file2);
for (AbstractFile abstractFile : list) {
abstractFile.delete();
}
复制代码
已上诉文件目录例子为例,则会致使在一个文件集合中须要针对性的判断该文件类型:
Directory directory = new Directory("目录");
File file1 = new File("文件1");
File file2 = new File("文件2");
List<Object> list = new ArrayList<>();
list.add(directory);
list.add(file1);
list.add(file2);
for (Object object : list) {
if (object instanceof File) {
//....
}
if (object instanceof Directory) {
//....
}
}
复制代码
使得使用方可以以一致性的方式调用接口,来处理对象,而没必要关心这个对象是单个叶节点仍是一个组合的对象结构。
安全性与透明性的考虑
有一系列对象集合,而且这些对象集合有明显的"总体/部分"的关系,能够构建成树形结构。