Home 组合模式
Post
Cancel

组合模式

Composite Pattern 对象结构型模式

意图

将对象组合成树型结构以表示 “部分 - 整体” 的层次结构。Composite 使得用户对单个对象和组合对象的使用具有一致性

结构

组合模式

其中:

  • Component 为组合中的对象声明接口;在适当情况下实现所有类共有接口的默认行为;声明一个接口用于访问和管理 Component 的子组件;(可选) 在递归结构中定义一个接口,用于访问一个父组件,并在合适的情况下实现它
  • Leaf 在组合中表示叶结点对象,叶结点没有子结点;在组合中定义图元对象的行为
  • Composite 定义所有子组件的那些组件的行为;存储子组件;在 Component 接口中实现与子组件有关的操作
  • Client 通过 Component 接口操纵组合组件的对象

适用性

Composite 模式适用于:

  • 想表示对象的部分 - 整体层次结构
  • 希望用户忽略组合对象与单个对象的不同,用户将统一地使用组合结构中的所有对象

例子 1

某公司的组织结构图如下图所示

组合模式-例1-图1

现采用组合(Composition)设计模式来构造该公司的组织结构,得到如下图所示的类图

组合模式-例1-图2

其中Company为抽象类,定义了在组织结构图上添加(Add)和删除(Delete)分公司/办事处或者部门的方法接口。类ConcreteCompany表示具体的分公司或者办事处,分公司或办事处下可以设置不同的部门。类HRDepartment和FinanceDepartment分别表示人力资源部和财务部

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
import java.util.*;

abstract class Company {
    protected String name;
    public Company(String name) {   this.name   = name; }
    public abstract void Add(Company c); // 增加子公司、办尊处或部门
    public abstract void Delete(Company c); // 删除子公司、办事处或部门
}

class ConcreteCompany extends Company {
    private List<Company> children = new ArrayList<Company>();
    // 存储子公司、办事处或部门
    public ConcreteCompany(String name) { super(name); }
    public void Add(Company c) {children.add(c); }
    public void Delete(Company c) {children.remove(c); }
}

class HRDepartment extends Company {
    public HRDepartment(String name) { super(name); }
// 其他代码省略
}

class FinanceDepartment extends Company {
    public FinanceDepartment(String name) { super(name); }
// 其他代码省略
}

public class Test {
    public static void main(String[] args) {
        ConcreteCompany root = new ConcreteCompany("北京总公司");
        root.Add(new HRDepartment("总公司人力资源部"));
        root.Add(new FinanceDepartment("总公司财务部"));

        ConcreteCompany comp = new ConcreteCompany("上海分公司");
        comp.Add(new HRDepartment("上海分公司人力资源部"));
        comp.Add(new FinanceDepartment("上海分公司财务部"));
        root.Add(comp);

        ConcreteCompany comp1 = new ConcreteCompany("南京办事处");
        comp1.Add(new HRDepartment("南京办事处人力资源部"));
        comp1.Add(new FinanceDepartment("南京办事处财务部"));
        comp.Add(comp1); // 其他代码省略
    }
}

例子 2

层叠菜单是窗口风格的软件系统中经常采用的一种系统功能组织方式。层叠菜单中包含的可能是一个菜单项(直接对应某个功能),也可能是一个子菜单,现在采用组合(composite)设计模式实现层叠菜单,得到如下图所示的类图

组合模式-例2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
import java.util.*;

abstract class MenuComponent {  // 构成层叠菜单的元素
    protected String name;      // 菜单项或子菜单名称
    public void printName() { System.out.println(name); }
    public abstract boolean addMenuElement(MenuComponent element) ;
    public abstract boolean removeMenuElement(MenuComponent element);
    public abstract List<MenuComponent> getElement();
}

class MenuItem extends MenuComponent {
    public MenuItem(String name) { this.name=name; }
    public boolean addMenuElement(MenuComponent element) { return false; }
    public boolean removeMenuElement(MenuComponent element) {
        return false;
    }
    public List<MenuComponent> getElement(){ return null; }
}

class Menu extends MemuComponent {
    private   List<MenuComponent> elementsList;
    public Menu(String name) {
        this.name = name;
        this.elementList = new ArrayList<MenuComponent>;
    }
    public boolean addMenuElement(MenuComponent element) {
        return elementList.add(element);
    }
    public boolean removeMenuElement(MenuComponent element) {
        return elementList.remove(element);
    }
    public List<MenuComponent> getElement() { return elementList; }
}

class CompositeTest {
    public static void main(String[] args) {
        MenuComponent mainMenu = new Menu("Insert");
        MenuComponent subMenu = new Menu("Chart");
        MenuComponent element = new MenuItem("On This Sheet");
        mainMenu.addMenuElement(subMenu);
        subMenu.addMenuElement(element);
        printMenus(mainMenu);
    }

    private static void printMenus(MenuComponent ifile) {
        ifile.printName();
        List<MenuComponent> children = ifile.getElement();
        if (children == null) return;
        for(MenuComponent element; children) {
            printMenus(element);
        }
    }
}

例子 3

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
import java.util.*;
public class CompositePattern {
    public static void main(String[] args) {
        AbstractFile root = new Folder("root");

        AbstractFile bin = new Folder("bin");
        AbstractFile tmp = new Folder("tmp");
        AbstractFile file = new File("file");

        root.Add(bin);
        bin.Add(tmp);
        root.Add(file);

        // root.Remove(tmp);
        print(root);
    }

    static void print(AbstractFile file){
        List<AbstractFile> lf = file.GetChildren();

        file.Operation();

        // 更简洁的方法
        // if(lf == null) return;
        // for(AbstractFile i : lf) print(i);

        if (lf != null) {
            for (AbstractFile i : lf) {
                if (i != null) {
                    print(i);
                }
                else
                    return;
            }
        }
        else
            return;
    }
}

abstract class AbstractFile{
    protected String name;

    public void Operation(){
        System.out.println(name);
    }

    public abstract boolean Add(AbstractFile af);
    public abstract boolean Remove(AbstractFile af);
    public abstract List<AbstractFile> GetChildren();
}

class Folder extends AbstractFile{
    private List<AbstractFile> childrenList = new ArrayList<>();

    public Folder(String name){
        this.name = name;
    }

    @Override
    public boolean Add(AbstractFile af){
        return childrenList.add(af);
    }

    @Override
    public boolean Remove(AbstractFile af){
        return childrenList.remove(af);
    }

    @Override
    public List<AbstractFile> GetChildren(){
        return this.childrenList;
    }
}

class File extends AbstractFile{
    public File(String name){
        this.name = name;
    }

    @Override
    public boolean Add(AbstractFile af){
        System.out.println("Forbidden");
        return false;
    }

    @Override
    public boolean Remove(AbstractFile af){
        System.out.println("Forbidden");
        return false;
    }

    @Override
    public List<AbstractFile> GetChildren(){
        return null;
    }
}
This post is licensed under CC BY 4.0 by the author.

桥接模式

装饰器模式