1. 什么是组合模式?
《Head First设计模式》中定义:允许将对象组成树形结构来表现“整体-部分”的层次结构。组合模式能让客户以一致的方式处理个别对象和对象组合。
为了保持透明性,组合模式内部所有的对象都必须实现同一个接口,否则客户就必须操心哪个对象是用的哪个接口,这就失去了组合模式的意义。
组合模式又称为“整体-部分模式”,是一种对象结构型模式。
2. 角色
公共抽象组件(Component):为组合模式中的所有对象提供一个统一的接口。
叶子组件(Leaf):叶子组件没有孩子。实现Component接口中的部分方法。
容器组件(Composite):具有子节点,子节点可以是叶子节点也可以是容器节点。实现Component部分方法。
图片来源于《Head First设计模式》
3. 优点
(1)客户端可以一致的使用一个容器组件和叶子组件,不必关心两者之间的差别。
(2)组合模式中新增容器组件和叶子组件都无需更改原有代码,符合开闭原则。
(3)组合模式为树状数据结构提供了一种解决方案。
4. 缺点
(1)无法限制组合组件中的子组件类型。在需要检测组件类型时,不能依靠编译期的类型约束来实现,必须在运行期间动态检测
5. 使用场景
(1)在具有整体和部分层次结构中,希望通过一种方式忽略整体与部分的差异,客户端可以一致对待。
(2)当需要使用树状数据结构时。
(3)当系统能够分离出叶子组件、容器组件,且他们的类型不固定,需要增加新的类型时。
6. 示例代码
模拟一个文件系统的实例。
(1)公共抽象组件Component
/**
* 文件公共抽象类(相当于Component)
*/
public abstract class FileComponent {
//文件名称
private String fileName;
public FileComponent(String fileName) {
this.fileName=fileName;
}
public String getFileName() {
return fileName;
}
public void setFileName(String fileName) {
this.fileName = fileName;
}
/**
* 打印文件名,index用来显示文件层级结构
*/
public abstract void display(String index);
public abstract void add(FileComponent fileComponent);
public abstract void delete(FileComponent fileComponent);
}
(2)叶子组件leaf
/**
* 音乐文件类(相当于leaf)
*/
public class MusicFile extends FileComponent {
public MusicFile(String fileName) {
super(fileName);
}
@Override
public void display(String index) {
System.out.println(index+getFileName());
}
@Override
public void add(FileComponent fileComponent) {
//不支持此操作
throw new UnsupportedOperationException();
}
@Override
public void delete(FileComponent fileComponent) {
//不支持此操作
throw new UnsupportedOperationException();
}
}
/**
* 文本文件
*/
public class TxtFile extends FileComponent {
public TxtFile(String fileName) {
super(fileName);
}
@Override
public void display(String index) {
System.out.println(index+getFileName());
}
@Override
public void add(FileComponent fileComponent) {
throw new UnsupportedOperationException();
}
@Override
public void delete(FileComponent fileComponent) {
throw new UnsupportedOperationException();
}
}
(3) 容器组件 Composite
/**
* 文件夹类(相当于Composite)
*/
public class Folder extends FileComponent {
private List<FileComponent> fileList;
public Folder(String fileName) {
super(fileName);
fileList=new ArrayList<FileComponent>();
}
@Override
public void display(String index) {
System.out.println(index+"Folder:"+getFileName());
if(fileList!=null && fileList.size()>0){
Iterator<FileComponent> iterator=fileList.iterator();
while (iterator.hasNext()) {
FileComponent file = (FileComponent) iterator.next();
file.display(index+"--");
}
}
}
@Override
public void add(FileComponent fileComponent) {
fileList.add(fileComponent);
}
@Override
public void delete(FileComponent fileComponent) {
fileList.remove(fileComponent);
}
}
(4)测试代码
public static void main(String[] args) {
FileComponent fc=new Folder("音乐文件夹");
fc.add(new MusicFile("死了都要爱.MP3"));
fc.add(new MusicFile("四处漂泊.MP3"));
FileComponent fc2=new Folder("歌词文件夹");
fc2.add(new TxtFile("死了都要爱.txt"));
fc2.add(new TxtFile("四处漂泊.txt"));
fc.add(fc2);
fc.display("");
System.out.println("...............");
//删除子文件夹
fc.delete(fc2);
fc.display("");
}
(5)测试结果
Folder:音乐文件夹
--死了都要爱.MP3
--四处漂泊.MP3
--Folder:歌词文件夹
----死了都要爱.txt
----四处漂泊.txt
...............
Folder:音乐文件夹
--死了都要爱.MP3
--四处漂泊.MP3
【四川乐山程序员联盟,欢迎大家加群相互交流学习5 7 1 8 1 4 7 4 3】