Composite模式

在面向对象的系统中,我们经常会遇到一类具有“容器”特征的对象,即它们在充当对象的同时,又是其他对象的容器。

举例:在操作系统中,文件的概念很广泛,其中文件可以是普通文件,也可以是目录(Unix中,设备也是文件),目录中可以存放文件。如下图:

1

File :抽象的文件类

PhysicalFile :普通文件

Directory :目录

// File.java

package com.pnft.patterns.composite;

public abstract class File

{

public abstract void process();

}

// PhysicalFile.java

package com.pnft.patterns.composite;

public class PhysicalFile extends File

{

@Override

public void process()

{

System.out.println("Method process() in PhysicalFile...");

}

}

// Directory.java

package com.pnft.patterns.composite;

import java.util.ArrayList;

public class Directory extends File

{

private ArrayList<File> arrayList;

@Override

public void process()

{

System.out.println("Method process() in Directory...");

}

public Directory()

{

arrayList = new ArrayList<File>();

}

public ArrayList<File> getFiles()

{

return arrayList;

}

public void add(File file)

{

arrayList.add(file);

}

public void remove(File file)

{

if(file != null)

{

arrayList.remove(file);

}

}

}

// PatternClient.java

package com.pnft.patterns.composite;

import java.util.ArrayList;

import java.util.Iterator;

public class PatternClient

{

private void recursiveProcess(ArrayList<File> arrayList)

{

if (arrayList != null)

{

for(Iterator<File> iter = arrayList.iterator(); iter.hasNext();)

{

File f = iter.next();

if(f instanceof Directory)

{

((File)f).process();

recursiveProcess(((Directory) f).getFiles());

}

else

{

f.process();

}

}

}

}

public static void main(String[] args)

{

PatternClient pc = new PatternClient();

File f = new Directory();

f.process();

File f1 = new Directory();

File f2 = new PhysicalFile();

((Directory)f1).add(f2);

((Directory)f).add(f1);

File f3 = new Directory();

File f4 = new Directory();

File f5 = new Directory();

File f6 = new PhysicalFile();

((Directory)f5).add(f6);

((Directory)f4).add(f5);

((Directory)f3).add(f4);

((Directory)f).add(f3);

ArrayList<File> arrayList = ((Directory)f).getFiles();

pc.recursiveProcess(arrayList);

}

}

上述程序运行结果:

Method process() in Directory...

Method process() in Directory...

Method process() in PhysicalFile...

Method process() in Directory...

Method process() in Directory...

Method process() in Directory...

Method process() in PhysicalFile...

上面程序存在的问题:

在容器类Directory中存在一个getFiles方法,而该方法的存在直接导致了客户程序PatternClient中的recursiveProcess方法的存在,这说明Directory类和客户程序PatternClient的耦合太紧(另外还有,在PatternClient中要判断一个对象是Directory还是PhysicalFile,以便采取不同的处理方式等等,都说明了耦合过紧这一情况),而且recursiveProcess方法是递归的,这于程序的维护不甚合适。因此,问题的根源在于:客户代码过多地依赖对象容器的内部实现结构,对象容器内部实现结构(而非抽象接口)的变化将引起客户端嗲吗的频繁变化,从而带来了代码的维护性和扩展性等方面的弊端。

Composite设计模式就是将“客户代码与复杂的对象昂容器结构”解耦,让对象容器自己来实现自身的复杂结构,从而使得客户代码就像处理简单对象(PhysicalFile)一样来处理复杂的对象容器(Directory)

Composite设计模式的意图就是,将对象组合成属性结构以表示“部分-整体”的层次结构。它使得客户对单个对象和组合对象的使用具有一致性。-GoF

下面我们用Composite设计模式来解决上面的问题,具体代码如下,其中被注解的部分是未采用Composite设计模式之前的代码:

// File.java

package com.pnft.patterns.composite;

public abstract class File

{

public abstract void process();

public abstract void add(File file); // add方法抽象出来

public abstract void remove(File file); // remove方法抽象出来

}

// PhysicalFile.java

package com.pnft.patterns.composite;

public class PhysicalFile extends File

{

@Override

public void process()

{

System.out.println("Method process() in PhysicalFile...");

}

@Override

public void add(File file)

{

// add方法在本类中完全没有必要,但是为了将Directory类中的add方法抽取到File中,

// 因此必须在本类override这个add方法,这是为了在客户程序中不需要区分到底是

// Directory类还是PhysicalFile类,从而减少客户程序的依赖。

// 这个方法可以为空,或者抛出一个自定义的异常。

// 不很完美,因为在本类中被迫增加了这个方法。

}

@Override

public void remove(File file)

{

// remove方法在本类中完全没有必要,但是为了将Directory类中的remove方法抽取到File中,

// 因此必须在本类override这个add方法,这是为了在客户程序中不需要区分到底是

// Directory类还是PhysicalFile类,从而减少客户程序的依赖。

// 这个方法可以为空,或者抛出一个自定义的异常。

// 不很完美,因为在本类中被迫增加了这个方法。

}

}

// Directory.java

package com.pnft.patterns.composite;

import java.util.ArrayList;

import java.util.Iterator;

public class Directory extends File

{

private ArrayList<File> arrayList;

@Override

public void process()

{

System.out.println("Method process() in Directory...");

// 下面if块是按照Composite设计模式的要求新增加的代码

if(arrayList != null)

{

for(Iterator<File> iter = arrayList.iterator(); iter.hasNext();)

{

File f = (File)iter.next();

f.process();

}

}

}

public Directory()

{

arrayList = new ArrayList<File>();

}

// public ArrayList<File> getFiles() // 注释掉

// {

// return arrayList;

// }

public void add(File file)

{

arrayList.add(file);

}

public void remove(File file)

{

if(file != null)

{

arrayList.remove(file);

}

}

}

// PatternClient.java

package com.pnft.patterns.composite;

//import java.util.ArrayList; // 注释掉

//import java.util.Iterator;

public class PatternClient

{

// private void recursiveProcess(ArrayList<File> arrayList)

// {

// if (arrayList != null)

// {

// for(Iterator<File> iter = arrayList.iterator(); iter.hasNext();)

// {

// File f = iter.next();

// if(f instanceof Directory)

// {

// ((File)f).process();

// recursiveProcess(((Directory) f).getFiles());

// }

// else

// {

// f.process();

// }

// }

// }

// }

public static void main(String[] args)

{

// PatternClient pc = new PatternClient();

File f = new Directory();

// f.process();

File f1 = new Directory();

File f2 = new PhysicalFile();

// ((Directory)f1).add(f2); // 注释掉这2行代码,

// ((Directory)f).add(f1); // 用下面两行代码代替

f1.add(f2);

f.add(f1);

File f3 = new Directory();

File f4 = new Directory();

File f5 = new Directory();

File f6 = new PhysicalFile();

// ((Directory)f5).add(f6); // 注释掉这4行代码,

// ((Directory)f4).add(f5);

// ((Directory)f3).add(f4);

// ((Directory)f).add(f3); // 用下面4行代码代替

f5.add(f6);

f4.add(f5);

f3.add(f4);

f.add(f3);

f.process();

// ArrayList<File> arrayList = ((Directory)f).getFiles(); // 注释掉

// pc.recursiveProcess(arrayList);

}

}

各对象之间的关系:

图2

从上面程序看,PatternClient已经被简化了许多,而且仅对抽象类File有依赖(创建对象除外,创建对象可以用诸如Simple Factory设计模式来解决,在此不赘述)。上面程序运行结果:

Method process() in Directory...

Method process() in Directory...

Method process() in PhysicalFile...

Method process() in Directory...

Method process() in Directory...

Method process() in Directory...

Method process() in PhysicalFile...

可以看到,该结果和前面程序的结果是一致的。前后两个程序的重要区别:

1. 递归并没有消除,后者将递归移动到了Directory类中的process方法,同时虽然是递归,但是非常自然,易于理解;而前者需要客户程序来处理递归,从而从代码维护的角度而言存在很大的差别。

2. 后者将add方法和remove方法抽象到了File类中,从而保证在客户程序中不需要进行转型(Type Casting)处理,从而大幅度降低了客户程序代码复杂程度和对外部依赖程序。这样做的一个glitch就是,在PhysicalFile类中被迫增加了add方法和remove方法。

下面是后者的UML类图:

图3

实际上严格的Composite静态结构图如下:

4

同时一定要记住:

Leaf中的add方法和remove方法,必须为空(即不存在任何代码)或者只抛出异常即可。因为Leaf类是叶子类,不能作为容器,因此也就无从add或者remove了。

我们可以看到,图1的大致结构看起来和图2、图3没有太大的不同,它们之间的主要区别是:

1. 一些方法是否需要存在

2. 这些方法所处的位置

3. 方法内容不同

因此不能仅从静态结构图比较类似,就判定对应图1是实现的项目代码就符合Composite设计模式的。通过前面两个不同的Java项目的代码,可以非常清楚地看出这一点。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值