名称
组合模式(COMPOSITE)
目的
将对象组合成树形结构以表示“部分-整体”的层次结构。Composite使得用户对单个对象和组合对象的使用具有一致性。
适用性
以下情况使用Composite模式:
• 想要表示对象的部分-整体层次结构。
• 希望用户忽略组合对象与单个对象的不同,用户将统一地使用组合结构中的所有对象。
结构
典型的Composite对象结构如下图所示
组件(Component):
为组合中的对象声明接口。
在适当的情况下,实现所有类共有接口的缺省(默认)行为。
声明一个接口用于访问和管理Component的子组件。
(可选)在递归结构中定义一个接口,用于访问一个父部件,并在合适的情况下实现它。
叶子节点(Leaf):
在组合中表示叶节点对象,叶节点没有子节点。
在组合中定义图元对象的行为。
复合节点(Composite):
定义有子部件的那些部件的行为。
存储子部件。
在Component接口中实现与子部件有关的操作。
客户端(Client):通过Component接口操纵组合部件的对象。
协作
用户使用Component类接口与组合结构中的对象进行交互。
如果接收者是一个叶节点,则直接处理请求。
如果接收者是Composite,它通常将请求发送给它的子部件,在转发请求之前与/或之后可能执行一些辅助操作。
效果
优点
定义了包含基本对象和组合对象的类层次结构。基本对象可以被组合成更复杂的组合对象,而这个组合对象又可以被组合,这样不断的递归下去。客户代码中,任何用到基本对象的地方都可以使用组合对象。
简化客户代码。客户可以一致地使用组合结构和单个对象。通常用户不知道 (也不关心)处理的是一个叶节点还是一个组合组件。这就简化了客户代码 , 因为在定义组合的那些类中不需要写一些充斥着选择语句的函数。
使得更容易增加新类型的组件。新定义的Composite或Leaf子类自动地与已有的结构和客户代码一起工作,客户程序不需因新的Component类而改变。
符合设计原则:
符合单一职责原则,叶子组件和组合组件各司其职。
符合里氏替换原则,客户端可以用统一的接口处理单个对象或组合对象。
缺点
使设计变得更加一般化(通用)。容易增加新组件也会产生一些问题,那就是很难限制组合中的组件。有时希望一个组合只能有某些特定的组件。使用Composite时,不能依赖类型系统施加这些约束,而必须在运行时刻进行检查。
计复杂度增加。客户端需要花费更多时间理解类之间的层次关系。
不易用继承增加新功能。对于组合中的构件,使用继承的方式增加新功能可能会变得困难,因为这可能会破坏组合的结构
应用
问题
想要设计一个文件系统,系统中有文件夹和文件,为了方便我们抽象出组件File同时表示文件和文件夹,定义三种文件分别是图片、文本、视频,同时定义符合节点文件夹Folder。File中有名称和上级文件夹两个基本属性,同时定义了设置和访问父部件(Folder,这里用File也可以,只是因为父部件只会使文件夹不会是文件,所以用Folder)的方法,定义了访问和管理子部件的方法(display、remove、add,这里的方法参数必须是File,因为这些操作文件和文件夹都有可能),这样用户就可以使用File接口与组合结构中的对象进行交互。
UML
代码示例
组件:文件/文件夹
package com.ysj.part4.composite.component;
import com.ysj.part4.composite.composite.Folder;
public abstract class File {
private Folder parentFolder;
String name;
public File(String name) {
this.name = name;
}
public String getName() {
return name;
}
public Folder getParentFolder() {
return null != parentFolder ? parentFolder : new Folder("无父组件");
}
public void setParentFolder(Folder parentFolder) {
this.parentFolder = parentFolder;
}
/**
* 浏览文件夹内的文件和文件夹
*/
public abstract void display();
/**
* 添加文件和文件夹
*
* @param file
*/
public void add(File file) {
System.out.println(name + "不支持添加文件和文件夹");
}
/**
* 删除文件和文件夹
*
* @param file
*/
public void remove(File file) {
System.out.println(name + "不支持删除文件和文件夹");
}
}
叶子节点:图片、文本、视频
package com.ysj.part4.composite.leaf;
import com.ysj.part4.composite.component.File;
public class ImagerFile extends File {
public ImagerFile(String name) {
super(name);
}
public void display() {
System.out.println("这是图像文件,文件名:" + super.getName());
}
}
package com.ysj.part4.composite.leaf;
import com.ysj.part4.composite.component.File;
public class TextFile extends File {
public TextFile(String name) {
super(name);
}
public void display() {
System.out.println("这是文本文件,文件名:" + super.getName());
}
}
package com.ysj.part4.composite.leaf;
import com.ysj.part4.composite.component.File;
public class VideoFile extends File {
public VideoFile(String name) {
super(name);
}
public void display() {
System.out.println("这是影像文件,文件名:" + super.getName());
}
}
复合节点:文件夹
package com.ysj.part4.composite.composite;
import com.ysj.part4.composite.component.File;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;
import java.util.ArrayList;
import java.util.List;
public class Folder extends File {
private List<File> files;
public Folder(String name){
super(name);
files = new ArrayList<File>();
}
public void display() {
for(File file : files){
if (!(file instanceof Folder)){
file.display();
continue;
}
System.out.println("这是一个文件夹,文件夹名:" + file.getName());
}
}
public void add(File file){
// 所添加的文件设置父文件夹为本文件夹
file.setParentFolder(this);
files.add(file);
}
public void remove(File file){
files.remove(file);
}
}
已知应用
DOM (Document Object Model)
DOM 是一种用于表示 HTML 和 XML 文档的标准对象模型。DOM 树本身就是一种典型的组合模式应用,其中节点可以是元素节点、文本节点或其他类型的节点,而这些节点又可以组成树形结构。
文件系统
许多操作系统中的文件系统也是基于组合模式实现的。文件夹可以包含其他文件夹或文件,形成了树状结构。
评论区