谈谈对组合模式的看法

82 阅读4分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第21天,点击查看活动详情

组合模式

概叙

在计算机文件系统中,文件夹既可以放文件,也可以放文件夹(子文件夹)。在子文件夹中,一样既可以放文件,也可以放子文件夹。可以说,文件夹形成了一种容器结构、递归结构

虽然文件和文件夹是不同类型,但是都可以被放入文件夹中,。文件和文件夹有时也被统称为“目录条目”。在目录条目中,文件夹和文件被当作同一种对象看待(即一致性)。

将文件夹和文件都当作目录条目看待,将容器和内容作为同一种东西看待,可以帮助我们方便的处理问题。在容器中既可以放入内容,也可以放入小容器,在小容器中,又可以放入更小的容器。。。。。这样,就形成了容器结构、递归结构。

Composite模式就是用于创造这种结构的模式。能够时容器与内容具有一致性,创造递归结构。

示例程序

类的一览表

Entity抽象类,用来实现File和Directory的一致性File表示文件的类Directory表示文件夹的类Main测试入口

类图

img

代码

Entry类

抽象父类,File和Directory是其子类。

通过getName()获取名字,getSize()获取大小。

add()方法是向文件夹中放入文件(此处抛出异常,Directory需要将其覆盖)。

printList()为外部调用,printList(String prefix)为子类调用。

public abstract class Entity {
​
   public abstract String getName();    //获取名字
   public abstract int getSize();      //获取大小
   public Entity add(Entity entity) throws Exception {
     throw new RuntimeException("运行异常");       //加入目录条目
   }
​
   public void printList() {
     printList("");        //显示目录条目一览
   }
​
   protected abstract void printList(String prefix);  //为一览加上前缀
​
   @Override
   public String toString() {        //显示代表类文字
     return getName() + "(" + getSize() + ")";
   }
​
}

File类

name表示文件名,size表示文件大小。

实现printList(String prefix) 方法。

public class File extends Entity {
​
   private String name;
   private int size;
   
  public File(String name, int size) {
     this.name = name;
     this.size = size;
   }
  
   @Override
   public String getName() {
     return name;
   }
​
   @Override
   public int getSize() {
     return size;
   }
​
   @Override
   protected void printList(String prefix) {
     System.out.println(prefix + "/" + this);
   }
}

Directory类

表示文件夹的类,是Entity的子类。

name表示文件夹名,directory保存文件夹的目录条目(指定泛型Entity)。

getSize(),通过计算得出大小。size += entity.getSize()。entity不论是File的实例还是Directory的实例,我们都可以通过getSize得到它的大小。 这就是Composite模式的特征——“容器与内容的一致性”。

如果entity是Directory的实例,调用getSize()会将该文件夹下的所有目录条目大小加起来。如果还有子文件夹,又会调用getSize()方法,形成递归调用。getSize()方法的递归调用与Composite模式的结构是相对应的。

add方法用于向文件夹中加入子文件夹或文件。

public class Directory extends Entity {
​
   private String name;
​
   private ArrayList<Entity> directory = new ArrayList();
   public Directory(String name) {
     this.name = name;
   }
  
   @Override
   public String getName() {
     return name;
   }
​
   @Override
   public int getSize() {
     int size = 0;
     Iterator<Entity> it = directory.iterator();
     while (it.hasNext()) {
       Entity entity = it.next();
       size += entity.getSize();
     }
     return size;
   }
  
   @Override
   public Entity add(Entity entity) throws Exception {
     directory.add(entity);
     return this;
   }
  
   @Override
   protected void printList(String prefix) {
     System.out.println(prefix + "/" + this);
     Iterator<Entity> it = directory.iterator();
     while (it.hasNext()) {
       Entity entity = it.next();
       entity.printList(prefix + "/" + name);
     }
   }
}

Main类

public class Main {
​
   public static void main(String[] args) {
     try {
       System.out.println("making root entries......");
       Directory rootDir = new Directory("root");
       Directory binDir = new Directory("bin");
       Directory tmpDir = new Directory("tmp");
       Directory usrDir = new Directory("usr");
       rootDir.add(binDir);
       rootDir.add(tmpDir);
       rootDir.add(usrDir);
       binDir.add(new File("vi", 10000));
       binDir.add(new File("latex", 20000));
       rootDir.printList();
       System.out.println("");
       System.out.println("making usr entries......");
       Directory yuki = new Directory("yuki");
       Directory hanako = new Directory("hanako");
       Directory tomura = new Directory("tomura");
       usrDir.add(yuki);
       usrDir.add(hanako);
       usrDir.add(tomura);
       yuki.add(new File("diary.html", 100));
       hanako.add(new File("memo.txt", 200));
       tomura.add(new File("game.doc", 300));
       tomura.add(new File("junk.mail", 500));
       rootDir.printList();
     } catch (Exception e) {
       e.printStackTrace();
     }
   }
}
​
making root entries......
​
/root(30000)
​
/root/bin(30000)
​
/root/bin/vi(10000)
​
/root/bin/latex(20000)
​
/root/tmp(0)
​
/root/usr(0)
​
​
​
making usr entries......
​
/root(31100)
​
/root/bin(30000)
​
/root/bin/vi(10000)
​
/root/bin/latex(20000)
​
/root/tmp(0)
​
/root/usr(1100)
​
/root/usr/yuki(100)
​
/root/usr/yuki/diary.html(100)
​
/root/usr/hanako(200)
​
/root/usr/hanako/memo.txt(200)
​
/root/usr/tomura(800)
​
/root/usr/tomura/game.doc(300)
​
/root/usr/tomura/junk.mail(500)

登场角色

  1. Leaf(树叶)
  2. 表示内容角色,不能放入其他物品。(File)
  3. Composite(复合物)
  4. 表示容器角色,可以放入容器和树叶。(Directory)
  5. Component
  6. 使leaf与Composite具有一致性,是其父类(Entity)
  7. client
  8. 使用角色,(Main)

拓展

多个和单个的一致性

Composite模式可以使容器与内容具有一致性,也可称其为,多个和单个一致性,即将多个对象结合在一起,当作一个对象下使用。

add方法

  1. 定义在Entity中,默认报错
  2. 能够使用add方法的只有Directory
  3. 定义在Entity中,什么也不做
  4. 声明但不实现
  5. 声明抽象方法,子类实现。(这样的话File类中也必须定义完全没有用add方法)
  6. 定义在Directory中