组合模式也是结构模式之一,组合模式比较简单,它将一组相似的对象看作一个对象处理,并根据一个树状结构来组合对象,然后提供一个统一的方法去访问相应的对象,以此忽略掉对象与对象之间的差别。它的UML图如下
下面是它的模版代码
public abstract class Component {
protected String name;
public Component(String name) {
this.name = name;
}
/**
* 具体逻辑的方法由子类实现
*/
public abstract void doSomething();
}
public class Composite extends Component{
/**
* 存储节点的容器
* @param name
*/
private List components = new ArrayList<>();
public Composite(String name) {
super(name);
}
@Override
public void doSomething() {
System.out.println(name);
if (null != components){
for (Component c : components){
c.doSomething();
}
}
}
/**
* 添加子节点
* @param child
*/
public void addChild(Component child){
components.add(child);
}
/**
* 移除子节点
* @param child
*/
public void removeChild(Component child){
components.remove(child);
}
/**
* 获取子节点
* @param index
* @return
*/
public Component getChildren(int index){
return components.get(index);
}
}
public class Leaf extends Component{
public Leaf(String name) {
super(name);
}
@Override
public void doSomething() {
System.out.print(name);
}
}
public class Client {
public static void main(String[] args){
//构造一个根节点
Composite root = new Composite("Root");
//构造两个枝干节点
Composite branch1 = new Composite("Branch1");
Composite branch2 = new Composite("Branch2");
//构造两个叶子节点
Leaf leaf1 = new Leaf("Leaf1");
Leaf leaf2 = new Leaf("Leaf2");
//将叶子节点添加至枝干节点中
branch1.addChild(leaf1);
branch2.addChild(leaf2);
//将枝干节点添加至根节点中
root.addChild(branch1);
root.addChild(branch2);
//执行方法
root.doSomething();
}
}
角色介绍:
Component : 抽象根节点,为组合中的对象声明接口。
Composite : 定义有子节点的那些枝干节点的行为,存储子节点,在Component接口中实现与子节点有关的操作
Leaf : 在组合中表示叶子节点对象
Client : 通过Component接口操作组合节点的对象
组合模式的简单实现
下面我们以文件夹系统为例讲解一下组合模式的简单实现,整个文件夹系统如下所示
首先声明一个抽象类,表示文件或文件夹
public abstract class Dir {
/**
* 声明一个List成员变量存储文件夹下的所有元素
*/
protected List dirs = new ArrayList<>();
private String name; //当前文件夹名
public Dir(String name) {
this.name = name;
}
/**
* 添加一个文件或文件夹
*/
public abstract void addDir(Dir dir);
/**
* 移除一个文件或文件夹
*/
public abstract void rmDir(Dir dir);
/**
* 清除文件夹下面的所有元素
*/
public abstract void clear();
/**
* 清空文件夹下的所有元素
*/
public abstract void print();
/**
* 获取文件夹下所有的文件或子文件夹
*/
public abstract List getFiles();
/**
* 获取文件或文件夹名
*/
public String getName(){
return name;
}
}
public class File extends Dir{
public File(String name) {
super(name);
}
@Override
public void addDir(Dir dir) {
}
@Override
public void rmDir(Dir dir) {
}
@Override
public void clear() {
}
@Override
public void print() {
}
@Override
public List getFiles() {
return null;
}
}
public class Folder extends Dir{
public Folder(String name) {
super(name);
}
@Override
public void addDir(Dir dir) {
dirs.add(dir);
}
@Override
public void rmDir(Dir dir) {
dirs.remove(dir);
}
@Override
public void clear() {
dirs.clear();
}
@Override
public void print() {
System.out.print(getName()+"(");
Iterator iter = dirs.iterator();
while (iter.hasNext()){
Dir dir = iter.next();
dir.print();
if (iter.hasNext()){
System.out.print(",");
}
}
System.out.print(")");
}
@Override
public List getFiles() {
return dirs;
}
}
public class Client {
public static void main(String[] args){
//构造一个目录对象表示C盘根目录
Dir diskC = new Folder("C");
//C盘根目录下有一个文件ImbaMallLog.txt
diskC.addDir(new File("ImbaMallLog.txt"));
//C盘目录下还有3个子目录Windows,PrefLogs,Program File
Dir dirWin = new Folder("Windows");
//Windows目录下有文件explorer.exe
dirWin.addDir(new File("explorer.exe"));
diskC.addDir(dirWin);
//PerfLogs目录
Dir dirPer = new Folder("PerfLogs");
//PerfLogs目录下有文件null.txt
dirPer.addDir(new File("null.txt"));
diskC.addDir(dirPer);
//Program File 目录
Dir dirPro = new Folder("Program File");
//Program File 目录下有文件ftp.txt
dirPro.addDir(new File("ftp.txt"));
diskC.addDir(dirPro);
//打印出文件结构
diskC.print();
}
}
Android源码中的模式实现
在android中View和ViewGroup这种嵌套就是一种组合模式
为什么ViewGroup有容器的功能
要回答这个问题,就要先了解View类与ViewGroup的差别在哪,首先要知道ViewGroup继承自View类
public abstract class ViewGroup extends View implements ViewParent, ViewManager
从继承角度来说ViewGroup拥有View类所有非私有化方法,既然如此,两者差别就在于ViewGroup所实现的ViewParent和ViewManager接口上,而事实也是如此,ViewManager接口定义了addView, removeView等对子视图操作的方法.
public interface ViewManager{
public void addView(View view, ViewGroup.LayoutParams params);
public void updateViewLayout(View view, ViewGroup.LayoutParams params);
public void removeView(View view);
}
ViewParent则定义了刷新容器的接口requestLayout和其它一些焦点事件的处理的接口.
public interface ViewParent {
public void requestLayout();
public boolean isLayoutRequested();
public void requestTransparentRegion(View child);
public void invalidateChild(View child, Rect r);
public ViewParent invalidateChildInParent(int[] location, Rect r);
public ViewParent getParent();
public void requestChildFocus(View child, View focused);
}
另外View中比较重要的两个测绘流程的方法onMeasure和onDraw在ViewGroup中都没有被重写,相对于onMeasure方法,在ViewGroup增加了一些计算子view的方法,如measureChildren,measureChildrenWithMargins等,而对于onDraw方法,ViewGroup定义了一个dispatchDraw方法来调用每一个子View的onDraw方法,由此可见,ViewGroup真的像一个容器一样,其职责只是负责对子元素的操作而非具体的个体行为.