组合模式
前言
组合模式体现了整体与部分的关系,其典型的应用就是树形结构,其表示“整体-部分”的层次结构。
组合模式核心:
- 抽象构件角色:定义了叶子和容器构件的共同点
- 叶子构件角色:无子节点
- 容器构件角色:有容器特征,可以包含子节点
一、场景
使用Composite模式来实现文件系统的”文件/目录“结构,需要规划的类如下:
- Root:File与Folder的共同接口界面。
- Folder:目录。目录下有子目录、文件。
- File:文件。存在与目录之下。
- Client:测试类
二、具体实现
1.透明方式
(1)Root类
/**
* 统一Root接口
*/
interface Root {
public boolean addFile(Root file);
public boolean removeFile(Root file);
public List<Root> getFile();
public void display();
}
(2)File类
/**
* 文件类
*/
public class File implements Root {
String name;
public File(String name) {
this.name = name;
}
@Override
public boolean addFile(Root file) {
return false;
}
@Override
public boolean removeFile(Root file) {
return false;
}
@Override
public List<Root> getFile() {
return null;
}
@Override
public void display() {
System.out.println(" |___"+name);
}
}
(3)Folder类
public class Folder implements Root {
String name;
List<Root> folder;
public Folder(String name) {
this.name = name;
this.folder = new ArrayList<Root>();
}
@Override
public boolean addFile(Root file) {
return folder.add(file);
}
@Override
public boolean removeFile(Root file) {
return folder.remove(file);
}
@Override
public List<Root> getFile() {
return folder;
}
@Override
public void display() {
System.out.println(name);
for (Root root : folder) {
if (root instanceof Folder){
System.out.print("|___");
}
root.display();
}
}
}
(4)测试类
public class Client {
public static void main(String[] args) {
//构造一个文件目录
Root root1 = new Folder("c://");
Root root2 = new Folder("d://");
Root win = new Folder("windows");
Root sys = new Folder("system");
Root hw = new File("HelloWorld.java");
root1.addFile(win);
root1.addFile(sys);
win.addFile(hw);
root1.display();
root2.display();
}
}
结果:
优点:所有的构件都有相同的接口,客户端可以同等对待所有对象。
缺点:不够安全,因为树叶类对象和组合对象在本质上是有区别的,其树叶类对象没有下一层次的对象,方法add()、remove()、getChild()是没有意义的,可能在运行期出错。
2.安全方式
(1)实现抽象文件角色
/**
* 抽象文件角色
*/
public interface IRoot {
//返回自己的实例
IRoot getComposite();
//某个商业方法
void sampleOperation();
//获取深度
int getDeep();
//设置深度
void setDeep(int deep);
}
(2)实现文件夹角色
public class IFolder implements IRoot {
private String name;
private int deep;
private Vector<IRoot> componentVector = new Vector<>();
public IFolder(String name) {
this.name = name;
}
@Override
public IRoot getComposite() {
return this;
}
@Override
public void sampleOperation() {
System.out.println("执行业务方法!");
}
@Override
public int getDeep() {
return deep;
}
@Override
public void setDeep(int deep) {
this.deep = deep;
}
//增加一个文件或文件夹
public void add(IRoot iRoot){
componentVector.add(iRoot);
iRoot.setDeep(this.deep+1);
}
//删除一个文件或文件夹
public void remove(IRoot iRoot){
componentVector.removeElement(iRoot);
}
//返回直接子文件集合
public Vector getAllFile(){
return componentVector;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
(3)实现文件类
/**
* 文件
*/
public class IFile implements IRoot{
private String name ;
private int deep;
public IFile(String name) {
this.name = name;
}
@Override
public IRoot getComposite() {
return this;
}
@Override
public void sampleOperation() {
System.out.println("执行商业方法!");
}
@Override
public int getDeep() {
return deep;
}
@Override
public void setDeep(int deep) {
this.deep = deep;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
(4)测试类
public class Client2 {
public static String indentChar = "\t";
public static void main(String[] args) {
new Client2().test();
}
public void test(){
//根下文件及文件夹
IFolder root = new IFolder("树根");
IFolder b1_1 = new IFolder("1_枝1");
IFolder b1_2 = new IFolder("1_枝2");
IFolder b1_3 = new IFolder("1_枝3");
IFile l1_1 = new IFile("1_叶1");
IFile l1_2 = new IFile("1_叶2");
IFile l1_3 = new IFile("1_叶3");
//b1_2下的文件及文件夹
IFolder b2_1 = new IFolder("2_枝1");
IFolder b2_2 = new IFolder("2_枝2");
IFile l2_1 = new IFile("2_叶1");
//缔造树的层次关系
root.add(b1_1);
root.add(b1_2);
root.add(l1_1);
root.add(l1_2);
b1_2.add(b2_1);
b1_2.add(b2_2);
b1_2.add(l2_1);
root.add(l1_3);
root.add(b1_3);
//打印数的层次
outTree(root);
}
public void outTree(IFolder iFolder){
System.out.println(iFolder.getName());
iterateTree(iFolder);
}
public void iterateTree(IFolder ifolder){
Vector<IRoot> clist = ifolder.getAllFile();
for(Iterator<IRoot> it = clist.iterator();it.hasNext();){
IRoot em = it.next();
if(em instanceof IFolder){
IFolder cm = (IFolder) em;
System.out.println(getIndents(em.getDeep())+cm.getName());
iterateTree(cm);
}else {
System.out.println(getIndents(em.getDeep())+((IFile)em).getName());
}
}
}
/**
* 文件层次缩进字符串
* @param x
* @return
*/
public static String getIndents(int x){
StringBuilder sb = new StringBuilder();
for(int i=0;i<x;i++){
sb.append(indentChar);
}
return sb.toString();
}
}
结果:
优点:树叶类和合成类具有不同的接口,解决了透明方式的缺点,是个安全的方式。
缺点:不够透明
三、应用场景
- 操作系统的资源管理器
- GUI中的容器层次图-XML文件解析
- OA系统中,组织结构的处理
- Junit单元测试框架
·底层设计就是典型的组合模式,TestCase(叶子)、TestUnite(容器)、Test接口(抽象)
总结
组合模式是为了简化编程而提出的,在如下情况可以考虑使用组合模式:
– 需要描述对象的部分和整体的等级结构
– 需要客户端忽略掉个体构件和组合构件的区别。客户端必须平等对待所有的构件,包含个体构件和组合构件。
优点是很容易地增加新种类的构件,使客户端很容易设计,因为客户端不需要知道构件是树叶构件还是树枝构件;缺点是当使用组合模式后,用继承的方法来增加新的行为会很困难。