设计模式(十一)Composite模式

  Composite模式模式能够使容器与内容具有一致性,创造出递归结构。有时,与将文件夹和文件都作为目录条目看待一样,将容器和内容作为同一种东西看待,可以帮助我们方便地处理问题。在容器中既可以放入内容,也可以放入小容器,然后在那个小容器中,又可以放入更小的容器。这样,就形成了容器结构、递归结构。

  示例程序类图如上图。

 1 package bigjunoba.bjtu.composite;
 2 
 3 public abstract class Entry {
 4     public abstract String getName();                               // 获取名字
 5     public abstract int getSize();                                  // 获取大小
 6     public Entry add(Entry entry) throws FileTreatmentException {   // 加入目录条目
 7         throw new FileTreatmentException();
 8     }
 9     public void printList() {                                       // 为一览加上前缀并显示目录条目一览
10         printList("");
11     }
12     protected abstract void printList(String prefix);               // 为一览加上前缀
13     public String toString() {                                      // 显示代表类的文字
14         return getName() + " (" + getSize() + ")";
15     }
16 }

  Entry类是一个表示目录条目的抽象类。为了能将文件夹Directory和文件File统一起来,所以设计了Entry类。其中printList方法有两种形式,称为重载。其中带参数的printList方法可见性是protected,即只能被Entry类的子类调用。

 1 package bigjunoba.bjtu.composite;
 2 
 3 public class File extends Entry {
 4     private String name;
 5     private int size;
 6     public File(String name, int size) {
 7         this.name = name;
 8         this.size = size;
 9     }
10     public String getName() {
11         return name;
12     }
13     public int getSize() {
14         return size;
15     }
16     protected void printList(String prefix) {
17         System.out.println(prefix + "/" + this);
18     }
19 }

  File类是表示文件的类,调用构造函数,就会根据传入的文件名和文件大小生成文件实例。其中的this表示父类中的toString方法。

 1 package bigjunoba.bjtu.composite;
 2 
 3 import java.util.Iterator;
 4 import java.util.ArrayList;
 5 
 6 public class Directory extends Entry {
 7     private String name;                    // 文件夹的名字
 8     private ArrayList<Entry> directory = new ArrayList<Entry>();      // 文件夹中目录条目的集合
 9     public Directory(String name) {         // 构造函数
10         this.name = name;
11     }
12     public String getName() {               // 获取名字
13         return name;
14     }
15     
16     public int getSize() {                  // 获取大小
17         int size = 0;
18         Iterator<Entry> it = directory.iterator();
19         while (it.hasNext()) {
20             Entry entry = (Entry)it.next();
21             size += entry.getSize();
22         }
23         return size;
24     }
25     
26     public Entry add(Entry entry) {         // 增加目录条目
27         directory.add(entry);
28         return this;
29     }
30     
31     protected void printList(String prefix) {       // 显示目录条目一览
32         System.out.println(prefix + "/" + this);
33         Iterator<Entry> it = directory.iterator();
34         while (it.hasNext()) {
35             Entry entry = (Entry)it.next();
36             entry.printList(prefix + "/" + name);
37         }
38     }
39 }

  Directory类是表示文件夹的类。这里用泛型集合来保存文件夹中的目录条目。getSize方法返回集合中所有元素的大小的总和。

  这里注意size += entry.getSize();不管entry是哪个类的实例,都可以通过getSize方法得到它的大小。这就是该模式的特征--容器与内容一致性-的表现。

  注意这里使用了递归,如果entry是Directory类的实例,调用 entry.getSize()时会将该文件夹下的所有目录条目的大小加起来,如果还有子文件夹,又会调用子文件夹的getSize方法。即getSize方法的递归调用与Composite模式的结构是相对应的。

  add方法不会判断接收到的是文件夹还是文件,而是通过委托给ArrayList类来实现。printList方法也会递归调用,不多解释了。

1 package bigjunoba.bjtu.composite;
2 
3 public class FileTreatmentException extends RuntimeException {
4     public FileTreatmentException() {
5     }
6     public FileTreatmentException(String msg) {
7         super(msg);
8     }
9 }

  由于调用add方法抛出的异常类并非Java类库的自带异常类,所以要编写自己的异常类。

 1 package bigjunoba.bjtu.composite;
 2 
 3 public class Main {
 4     public static void main(String[] args) {
 5         try {
 6             System.out.println("Making root entries...");
 7             Directory rootdir = new Directory("root");
 8             Directory bindir = new Directory("bin");
 9             Directory tmpdir = new Directory("tmp");
10             Directory usrdir = new Directory("usr");
11             rootdir.add(bindir);
12             rootdir.add(tmpdir);
13             rootdir.add(usrdir);
14             bindir.add(new File("vi", 10000));
15             bindir.add(new File("latex", 20000));
16             rootdir.printList();
17 
18             System.out.println("");
19             System.out.println("Making user entries...");
20             Directory yuki = new Directory("yuki");
21             Directory hanako = new Directory("hanako");
22             Directory tomura = new Directory("tomura");
23             usrdir.add(yuki);
24             usrdir.add(hanako);
25             usrdir.add(tomura);
26             yuki.add(new File("diary.html", 100));
27             yuki.add(new File("Composite.java", 200));
28             hanako.add(new File("memo.tex", 300));
29             tomura.add(new File("game.doc", 400));
30             tomura.add(new File("junk.mail", 500));
31             rootdir.printList();
32         } catch (FileTreatmentException e) {
33             e.printStackTrace();
34         }
35     }
36 }

  Main类作为测试类,也不做过多解释。

Making root entries...
/root (30000)
/root/bin (30000)
/root/bin/vi (10000)
/root/bin/latex (20000)
/root/tmp (0)
/root/usr (0)

Making user entries...
/root (31500)
/root/bin (30000)
/root/bin/vi (10000)
/root/bin/latex (20000)
/root/tmp (0)
/root/usr (1500)
/root/usr/yuki (300)
/root/usr/yuki/diary.html (100)
/root/usr/yuki/Composite.java (200)
/root/usr/hanako (300)
/root/usr/hanako/memo.tex (300)
/root/usr/tomura (900)
/root/usr/tomura/game.doc (400)
/root/usr/tomura/junk.mail (500)

   测试结果如上图,一目了然。

原文地址:https://www.cnblogs.com/BigJunOba/p/8708329.html