介绍
组合(Composite)模式:将对象组合成树形结构以表示“部分-整体”的层次结构,使得用户对单个对象和组合对象的使用具有一致性。
组合模式也是结构模式之一,组合模式比较简单,它将一组相似的对象看作一个对象处理,并根据一个树状结构来组合对象,然后提供一个统一的方法去访问相应的对象,以此忽略掉对象与对象之间的差别。
优点
- 组合模式使得客户端代码可以一致地处理单个对象和组合对象,无须关心自己处理的是单个对象,还是组合对象,这简化了客户端代码。
- 更容易在组合体内加入新的对象,客户端不会因为加入了新的对象而更改源代码,满足“开闭原则”。
缺点
- 设计较复杂,客户端需要花更多时间理清类之间的层次关系。
- 不容易限制容器中的构件。
- 不容易用继承的方法来增加构件的新功能。
使用场景
- 表示对象的部分-整体层次结构时。
- 从一个整体中能独立出部分模块或功能的场景。
结构与实现
模式包含以下主要角色。
- Component:抽象根节点,为组合中的对象声明接口。
- Composite:定义有子节点的那些枝干节点的行为,存储子节点,在 Component 接口中实现与子节点有关的操作。
- Leaf:在组合中表示叶子节点对象。
- Client:通过 Component 接口操作组合节点的对象。
其结构图如下图所示。
下面是它的模版代码:
客户端测试代码:
示例
下面我们以文件夹系统为例讲解一下组合模式的简单实现,整个文件夹系统如下所示:
首先声明一个抽象类,表示文件或文件夹:
然后定义文件夹类和文件类:
像这样将组合所使用的方法定义在抽象类的方式称为透明的组合模式,遵循了依赖倒置原则,但却违反了单一职责原则与接口隔离原则。
而前面模板代码中所描述的组合模式则称为安全地组合模式,这种模式客户端在使用的时候必须依赖具体的实现,这违反了依赖倒置原则,但遵循了单一职责原则与接口隔离原则。
测试代码:
ANDROID 源码中的实现
Android 中的 View 和 ViewGroup 的嵌套组合是一个典型的组合模式实现,如下图所示。
在 Android 的这个视图层级中,容器一定是 ViewGroup,而且只有 ViewGroup 才能包含其他的 View,比如 LinearLayout 能包含 TextView、Button、CheckBox 等,但是反过来 TextView 是不能包含 LinearLayout 的,因为 TextView 直接继承于 View,其并非一个容器。
ViewGroup 是继承于 View 类的,但为什么有容器的功能呢?
从继承的角度来说,ViewGroup 拥有 View 类所有的非私有方法。既然如此,两者的差别就在于 ViewGroup 所实现的 ViewParent 和 ViewManager 接口上,而事实也是如此。
ViewManager 定义了 addView、removeView 等对子视图操作的方法。
而 ViewParent 则定义了刷新容器的接口 requestLayout 和其他一些焦点事件的处理的接口。
其中有一些方法比较常见,比如 requestLayout 和 bringChildToFront 等。
ViewGroup 除了所实现的这两个接口与 View 不一样外,还有重要的一点就是 ViewGroup 是抽象类,将 View 的 onLayout 重置为抽象方法。容器子类必须实现 onLayout 来布局定位。
除此之外,在 View 中比较重要的两个测绘流程的方法 onMeasure 和 onDraw 在 ViewGroup 中都没有被重写,相对于 onMeasure 方法,在 ViewGroup 中增加了一些计算子 View 的方法,如 measureChildren、measureChildrenWithMargins 等;而对于 onDraw 方法,ViewGroup 定义了一个 dispatchDraw 方法来调用其每一个子 View 的 onDraw 方法,由此可见,ViewGroup 真的就象一个容器一样,其职责只是负责对子元素的操作而非具体的个体行为。