本文首发于cdream的我的博客,点击得到更好的阅读体验!html
欢迎转载,转载请注明出处。java
本文简单介绍组合模式,以系统文件和文件夹为例。git
<!--more-->程序员
定义:组合模式(Composite pattern)将对象整合到树状结构中来表示总体/部分的层次关系,在树状结构中包括对象和对象组合。组合模式能让客户用一致的方式处理个别对象的对象组合。github
<div class="note info"><strong>组合模式主要解决两个问题</strong>:<br>1.部分-总体的层级结构以树状结构来表现<br>2.部分总体的层级结构中,对象和对象组合要以一致方式处理</div>设计模式
UML:安全
主要角色:ide
抽象构件类(Compnent):为全部对象定义了一个接口,不管是叶节点仍是组合。测试
叶构件类(Leaf):继承了抽象构件类,可是没有子构件了,虽然继承了add,remove,getChildren方法,可是对于叶节点来讲没什么意义,不过为了保持透明性,咱们坚持这么作。this
树枝构件类(Component):继承了抽象构件类,持有一个子构件类容器口。
系统文件目录是一个典型的包括叶构件(文件)和树枝构件(文件夹)的组合模式。
固然文件夹和文件操做上仍是有区别的,不过,我在这里就让文件夹和文件都实现相同的接口,完成最理想的组合的组合模式——放弃安全性,追求透明性。
定义一个抽象接口,为了保证能够使用户不关心到底是什么,全部文件/文件夹都要实现这个接口。
public abstract class FileInterface { // 添加文件 void add(FileInterface file) { throw new UnsupportedOperationException(); } // 删除文件 void remove(String fileName) { throw new UnsupportedOperationException(); } // 获取文件 Set<FileInterface> getChildren() { throw new UnsupportedOperationException(); } // 获取文件名字 String getName(){ throw new UnsupportedOperationException(); } // 获取文件描述 String getDescription(){ throw new UnsupportedOperationException(); }; // 运行程序 void run(){ throw new UnsupportedOperationException(); }; }
写一个文件夹类
public class Directory implements FileInterface { private String name; private String desc; // 文件夹要持有文件列表 private Set<FileInterface> set = new HashSet<>(); public Directory(String name, String desc) { this.name = name; this.desc = desc; } @Override public void add(FileInterface file) { set.add(file); } @Override public void remove(String fileName) { if (fileName==null) throw new UnsupportedOperationException("请输入文件名"); Iterator<FileInterface> iterator = this.set.iterator(); while (iterator.hasNext()){ FileInterface next = iterator.next(); if (fileName.equals(next.getName())){ iterator.remove(); } } } @Override public Set<FileInterface> getChildren() { return this.set; } @Override public String getName() { return this.name; } @Override public String getDescription() { return this.desc; } // 重写了equals和hashcode方法,不容许出现重名文件 @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof FileInterface)) return false; FileInterface directory = (FileInterface) o; return name.equals(directory.getName()); } @Override public int hashCode() { return name.hashCode(); } @Override public String toString() { return "Directory{" + "name='" + name + '\'' + ", desc='" + desc + '\'' + ", set=" + set + '}'; } }
写一个exe文件类
public class ExeFile implements FileInterface { private String name; private String desc; public ExeFile(String name, String desc) { this.name = name; this.desc = desc; } @Override public String getName() { return this.name; } @Override public String getDescription() { return this.desc; } @Override public void run() { System.out.println("运行程序"); } @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof FileInterface)) return false; FileInterface directory = (FileInterface) o; return name.equals(directory.getName()); } @Override public int hashCode() { return name.hashCode(); } @Override public String toString() { return "ExeFile{" + "name='" + name + '\'' + ", desc='" + desc + '\'' + '}'; } }
这两个类都继承了FileIntercepter抽象类并实现了全部方法,能够等同看待,虽然有些方法并非两个类都能用的,必定程度上丧失了安全性(程序员调用时须要指到方法的具体实现),不过为了保持透明性(方法同等看待),咱们仍选择了所有实现,对于个别方法使用抛出不支持操做异常处理!
下面看测试类
public class Test { public static void main(String[] args) { Directory c = new Directory("C:", "C盘啊"); ExeFile system = new ExeFile("system.exe", "系统程序"); c.add(system); Directory animals = new Directory("animals", "动物程序文件夹"); ExeFile dog = new ExeFile("dog.exe", "小狗程序"); ExeFile pig = new ExeFile("pig.exe","小猪程序"); animals.add(dog); animals.add(pig); c.add(animals); animals.remove("dog.exe"); System.out.println(c); } } ---->结果 Directory{name='C:', desc='C盘啊', set=[ExeFile{name='system.exe', desc='系统程序'}, Directory{name='animals', desc='动物程序文件夹', set=[ExeFile{name='pig.exe', desc='小猪程序'}]}]}
优势:
缺点:
组合的适用场合:
组合模式是对象的结构模式,重点掌握树状结构和将叶节点和组合节点同等看待。
当组合模式与迭代器模式结合时,调用者甚至能够忽略掉组合模式的结构,这里我必须推荐<font color="purple"><i>Head First 设计模式</i></font>中迭代器模式与组合模式这一章,你会发现组合模式与迭代器结合的巨大威力!
此外也有人会为了安全性,将非共性方法不在抽象类中声明,这样虽然安全了,可是调用者就不能将组合模式的叶节点与组合节点同等看待,这并不符合咱们的目的,因此咱们仍是选择本文这种实现方式。