《一天一模式》— 组合模式

1、组合模式的概念

组合模式,将对象组合成树形结构以表示“部分-总体”的层次结构,组合模式使得用户对单个对象和组合对象的使用具备一致性。掌握组合模式的重点是要理解清楚 “部分/总体” 还有 ”单个对象“ 与 "组合对象" 的含义。 java

听懂了这句话就不用往下看了,说明你会了。bash

听不懂我以为也正常,若是用一句话能学会就没人看书了。像我这种笨人,都是学会了一个模式,而后往它的定义上套。函数

2、何时使用组合模式

  • 须要某种树形结构,能够容纳菜单、子菜单和菜单项;
  • 须要可以在各个菜单之间随意游走,同级的菜单类型能够不一样;
  • 须要可以在遍历时,随意遍历各个菜单;

也就是说,树形结构中的菜单,子菜单是容器,菜单项是内容。this

组合模式是要让容器和内容具备一致性,创造出递归结构,而且在某一个场景下,容器和内容能够无差异使用,图中第二层既能够是Leaf,也能够是Node。spa

常见的场景有:部门树、功能菜单树、字典树等等,在遇到可递归的树形结构需求时,均可以考虑一下是否须要使用组合模式。code

3、怎么使用组合模式

以一个需求为例进行说明,仍是汽车相关的。component

汽车厂商将汽车分为品牌、车系、车型、他们以前是属性关系,品牌下有多个车系,车系下有多个车型,具体例子:对象

品牌是奔驰。blog

奔驰下有多个车系,A级,C级,E级。继承

每一个车系下有多个车型,A级下有A180,A200,A220。

需求是,打印出全部的车系和车型,在打印时进行名称美化,传入车系和车型均可以完成名称美化。

3.1 不使用组合模式带来的弊端

不使用组合模式来建立这个结构。

  • 首先建立一个品牌对象(Brand),里面有一个车系(Series)的ArrayList集合;
  • 而后建立一个车系对象(Series),里面有一个车型(Model)的ArrayList集合;
  • 最后建立一个车系对象(Model);

一个典型的一对多关系,具体代码以下:

/**
 * 品牌
 */
public class Brand {

    private List<Series> series = new ArrayList<Series>();

    private String name;

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

    public String getName() {
        return this.name;
    }

    public void add(Series s) {
        series.add(s);
    }

    public void remove(Series s) {
        series.remove(s);
    }

    public Series getChild(int i) {
        return series.get(i);
    }

    public void print() {
        System.out.println(getName());
        Iterator<Series> iterator = series.iterator();
        while(iterator.hasNext()) {
            iterator.next().print();
        }
    }

}

/**
 * 车系
 */
public class Series {

    private List<Model> model = new ArrayList<Model>();

    private String name;

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

    public String getName() {
        return this.name;
    }

    public void add(Model m) {
        model.add(m);
    }

    public void remove(Model m) {
        model.remove(m);
    }

    public Model getChild(int i) {
        return model.get(i);
    }

    public void print() {
        System.out.println(getName());
        Iterator<Model> iterator = model.iterator();
        while (iterator.hasNext()) {
            iterator.next().print();
        }
    }

}

/**
 * 车型
 */
public class Model {

    private String name;

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

    public String getName() {
        return this.name;
    }

    public void print() {
        System.out.println(getName());
    }

}

/**
 * 运行输出
 * 奔驰
 * 奔驰A系
 * A100
 * A200
 * 奔驰C系
 * C100
 * C200
 */
public class Client {

    public static void main(String[] args) {
        Brand brand = new Brand("奔驰");
        Series series = new Series("奔驰A系");
        brand.add(series);
        Model a100 = new Model("A100");
        Model a200 = new Model("A200");
        series.add(a100);
        series.add(a200);
        Series series1 = new Series("奔驰C系");
        brand.add(series1);
        Model c100 = new Model("C100");
        Model c200 = new Model("C200");
        series1.add(c100);
        series1.add(c200);
        brand.print();
    }

}

需求是,打印出全部的车系和车型,在打印时进行名称美化,传入车系和车型均可以完成名称美化。

第一个弊端 — 通用性差

这里要写这个函数就有问题,因为车系和车型的类型不一样,因此要单独编写函数,实现基本一致:

public String beautiful(Series series) {
    return "★" + series.getName() + "★";
}

public String beautiful(Model model) {
    return "★" + model.getName() + "★";
}

第二个弊端 — 结构不灵活

如今需求有变动:

车系是一个树形结构,也就是说Series对象的子对象中,能够存放车型,也能够存放车系。

回顾一下Series的代码:

public class Series {

    private List<Model> model = new ArrayList<Model>();

    ......

}

用于存放子对象的集合,泛型类型是Model,不能保存Series对象。

3.2 使用组合模式带来解决这个问题

直接上类图,使用组合模式后的类图以下:

回顾概念:

  • 将对象组合成树形结构以表示“部分-总体”的层次结构
  • 组合模式使得用户对单个对象和组合对象的使用具备一致性

如今,品牌、车系、车型都继承自组件(Component)。品牌中的集合,泛型类型是Component,车系中的集合泛型类型也是Component。这样就作到了概念中的两点,对象仍是树形结构来表示部分和总体。同时,因为对象自己继承自Component,它内部的子对象集合也是Component,因此作到了一致性。

下面是组合模式下的代码:

/**
 * 组件对象
 */
public abstract class Component {

    public String getName() {
        throw new UnsupportedOperationException();
    }

    public void add(Component component) {
        throw new UnsupportedOperationException();
    }

    public void remove(Component component) {
        throw new UnsupportedOperationException();
    }

    public Component getChild(int i) {
        throw new UnsupportedOperationException();
    }

    public void print() {
        throw new UnsupportedOperationException();
    }

    public String beautiful(Component component) {
        return "★" + component.getName() + "★";
    }

}

/**
 * 品牌,继承自Component
 */
public class Brand extends Component {

    // 内部不在是存放具体类型,如今改为了Component类型,继承自Component的对象均可以加入
    private List<Component> series = new ArrayList<Component>();

    private String name;

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

    public String getName() {
        return this.name;
    }

    public void add(Component component) {
        series.add(component);
    }

    public void remove(Component component) {
        series.remove(component);
    }

    public Component getChild(int i) {
        return series.get(i);
    }

    public void print() {
        System.out.println(getName());
        Iterator<Component> iterator = series.iterator();
        while(iterator.hasNext()) {
            iterator.next().print();
        }
    }

}

/**
 * 车系,继承自Component
 */
public class Series extends Component {

    // 内部不在是存放具体类型,如今改为了Component类型,继承自Component的对象均可以加入
    private List<Component> model = new ArrayList<Component>();

    private String name;

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

    public String getName() {
        return this.name;
    }

    public void add(Component component) {
        model.add(component);
    }

    public void remove(Component component) {
        model.remove(component);
    }

    public Component getChild(int i) {
        return model.get(i);
    }

    public void print() {
        System.out.println(beautiful(this));
        Iterator<Component> iterator = model.iterator();
        while (iterator.hasNext()) {
            iterator.next().print();
        }
    }

}

/**
 * 车型
 */
public class Model extends Component {

    private String name;

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

    public String getName() {
        return this.name;
    }

    public void print() {
        System.out.println(beautiful(this));
    }

}

组合模式的使用方式:

public class Client {

    public static void main(String[] args) {
        // 建立品牌
        Component brand = new Brand("奔驰");
        // 建立车系A
        Component series = new Series("奔驰A系");
        // 把车系A加到品牌
        brand.add(series);
        // 建立两个车型
        Component a100 = new Model("A100");
        Component a200 = new Model("A200");
        series.add(a100);
        series.add(a200);

        // 建立车系C
        Component series1 = new Series("奔驰C系");
        // 加到品牌
        brand.add(series1);

        // 在建立2个车型添加到车系C
        Component c100 = new Model("C100");
        Component c200 = new Model("C200");
        series1.add(c100);
        series1.add(c200);
        // 这里!在车型c100和c200平级的节点,建立一个子车系
        Component series2 = new Series("奔驰C系运动款");
        series1.add(series2);
        // 在子车系下建立2个车型
        Component c100s = new Model("C100S");
        Component c200s = new Model("C200S");
        series2.add(c100s);
        series2.add(c200s);
        // 输出整个的树
        brand.print();
    }

}

// 输出结果
奔驰
★奔驰A系★
★A100★
★A200★
★奔驰C系★
★C100★
★C200★
★奔驰C系运动款★
★C100S★
★C200S★

用组合模式解决了需求中的2个点:

1.使用一个函数操做车系,车型

// 可使用一个方式来操做车系、车型、品牌等对象,只要是这个树结构下的节点都可以操做
public String beautiful(Component component) {
    return "★" + component.getName() + "★";
}

2.随时能够添加子节点

// 在建立2个车型添加到车系C
Component c100 = new Model("C100");
Component c200 = new Model("C200");
series1.add(c100);
series1.add(c200);
// 这里!在车型c100和c200平级的节点,建立一个子车系
Component series2 = new Series("奔驰C系运动款");

输出结果是:

★奔驰C系★(车系)
  |-★C100★(车型)
  |-★C200★(车型)
  |-★奔驰C系运动款★(车系)
     |-★C100S★(车型)
     |-★C200S★(车型)

树的任意一级能够存听任意的对象(只要是继承自Component),这种方式解决了第二个需求。

4、总结

组合模式,个人一个简单理解是,在两个场景下应该去考虑使用:

  1. 当你的对象结构是树,树的各个层级并非有一个对象组合而成的,但这些对象都有相同的业务操做(上面的例子中的display方法);
  2. 面相后续的需求,有可能每一层保存的对象类型也不必定相同(例如上面的例子中,车系下不仅有车型,还有子车系);

组合模式的作法就很简单了,主要也是两点:

  1. 树上的全部节点都继承一个Component对象(抽象对象);
  2. 对象种的ArrayList的泛型对象使用Component;

以上就是组合模式的一些理解, 有不足之处请你们矫正,谢谢。

相关文章
相关标签/搜索