组合模式,将对象组合成树形结构以表示“部分-总体”的层次结构,组合模式使得用户对单个对象和组合对象的使用具备一致性。掌握组合模式的重点是要理解清楚 “部分/总体” 还有 ”单个对象“ 与 "组合对象" 的含义。 java
听懂了这句话就不用往下看了,说明你会了。bash
听不懂我以为也正常,若是用一句话能学会就没人看书了。像我这种笨人,都是学会了一个模式,而后往它的定义上套。函数
也就是说,树形结构中的菜单,子菜单是容器,菜单项是内容。this
组合模式是要让容器和内容具备一致性,创造出递归结构,而且在某一个场景下,容器和内容能够无差异使用,图中第二层既能够是Leaf,也能够是Node。spa
常见的场景有:部门树、功能菜单树、字典树等等,在遇到可递归的树形结构需求时,均可以考虑一下是否须要使用组合模式。code
以一个需求为例进行说明,仍是汽车相关的。component
汽车厂商将汽车分为品牌、车系、车型、他们以前是属性关系,品牌下有多个车系,车系下有多个车型,具体例子:对象
品牌是奔驰。blog
奔驰下有多个车系,A级,C级,E级。继承
每一个车系下有多个车型,A级下有A180,A200,A220。
需求是,打印出全部的车系和车型,在打印时进行名称美化,传入车系和车型均可以完成名称美化。
不使用组合模式来建立这个结构。
一个典型的一对多关系,具体代码以下:
/** * 品牌 */ 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对象。
直接上类图,使用组合模式后的类图以下:
回顾概念:
如今,品牌、车系、车型都继承自组件(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),这种方式解决了第二个需求。
组合模式,个人一个简单理解是,在两个场景下应该去考虑使用:
组合模式的作法就很简单了,主要也是两点:
以上就是组合模式的一些理解, 有不足之处请你们矫正,谢谢。