组合模式(Composite)是针对由多个节点对象(部分)组成的树形结构的对象(整体)而发展出的一种结构型设计模式,它能够使客户端在操作整体对象或者其下的每个节点对象时做出统一的响应,保证树形结构对象使用方法的一致性,使客户端不必关注对象的整体或部分,最终达到对象复杂的层次结构与客户端解耦的目的。
组合模式的核心思想是将对象看作是一个树形结构,其中每个节点可以是一个单独的对象(叶子节点)或者一个包含其他节点的容器(组合节点)。叶子节点和组合节点都实现了相同的接口,这样客户端就可以对它们进行一致的操作,而不需要关心它们的具体类型。
组合模式有以下几个角色:
组合模式的好处有:
组合模式的坏处有:
组合模式是一种将对象组合成树形结构的设计模式,它可以表示整体-部分的层次关系,并且提供了一致的接口来操作单个对象和对象组合。应用场景有:
假设我们有一个文件系统,其中有两种类型的文件:文本文件和文件夹。文本文件是叶子节点,文件夹是组合节点,可以包含其他文件。我们想要使用组合模式来实现文件系统的层次结构,并且提供一个打印文件路径的方法。代码如下:
定义抽象组件
public interface File {
// 获取文件名称
String getName();
// 添加子文件
void add(File file);
// 删除子文件
void remove(File file);
// 获取子文件
List<File> getChildren();
// 打印文件路径
void printPath(int space);
}
定义叶子节点
public class TextFile implements File {
private String name;
public TextFile(String name) {
this.name = name;
}
@Override
public String getName() {
return name;
}
@Override
public void add(File file) {
throw new UnsupportedOperationException("Text file cannot add child file");
}
@Override
public void remove(File file) {
throw new UnsupportedOperationException("Text file cannot remove child file");
}
@Override
public List<File> getChildren() {
throw new UnsupportedOperationException("Text file has no child file");
}
@Override
public void printPath(int space) {
StringBuilder sp = new StringBuilder();
for (int i = 0; i < space; i++) {
sp.append(" ");
}
System.out.println(sp + name);
}
}
定义组合节点
public class Folder implements File {
private String name;
private List<File> children;
public Folder(String name) {
this.name = name;
children = new ArrayList<>();
}
@Override
public String getName() {
return name;
}
@Override
public void add(File file) {
children.add(file);
}
@Override
public void remove(File file) {
children.remove(file);
}
@Override
public List<File> getChildren() {
return children;
}
@Override
public void printPath(int space) {
StringBuilder sp = new StringBuilder();
for (int i = 0; i < space; i++) {
sp.append(" ");
}
System.out.println(sp + name);
space += 2;
for (File child : children) {
child.printPath(space);
}
}
}
客户端代码
public class Client {
public static void main(String[] args) {
// 创建一个根文件夹,并添加两个文本文件和一个子文件夹
File root = new Folder("root");
root.add(new TextFile("a.txt"));
root.add(new TextFile("b.txt"));
File subFolder = new Folder("subFolder");
root.add(subFolder);
// 在子文件夹中添加两个文本文件
subFolder.add(new TextFile("c.txt"));
subFolder.add(new TextFile("d.txt"));
// 打印根文件夹的路径
root.printPath(0);
}
}
输出结果:
root
a.txt
b.txt
subFolder
c.txt
d.txt
package main
// importing fmt package
import (
"fmt"
)
// IComposite interface
type IComposite interface {
perform()
}
// Leaflet struct
type Leaflet struct {
name string
}
// Leaflet class method perform
func (leaf *Leaflet) perform() {
fmt.Println("Leaflet " + leaf.name)
}
// Branch struct
type Branch struct {
leafs []Leaflet
name string
branches []Branch
}
// Branch class method perform
func (branch *Branch) perform() {
fmt.Println("Branch: " + branch.name)
for _, leaf := range branch.leafs {
leaf.perform()
}
for _, branch := range branch.branches {
branch.perform()
}
}
// Branch class method add leaflet
func (branch *Branch) add(leaf Leaflet) {
branch.leafs = append(branch.leafs, leaf)
}
//Branch class method addBranch branch
func (branch *Branch) addBranch(newBranch Branch) {
branch.branches = append(branch.branches, newBranch)
}
//Branch class method getLeaflets
func (branch *Branch) getLeaflets() []Leaflet {
return branch.leafs
}
// main method
func main() {
var branch = &Branch{name: "branch 1"}
var leaf1 = Leaflet{name: "leaf 1"}
var leaf2 = Leaflet{name: "leaf 2"}
var branch2 = Branch{name: "branch 2"}
branch.add(leaf1)
branch.add(leaf2)
branch.addBranch(branch2)
branch.perform()
}
输出结果:
G:\GoLang\examples>go run composite.go
Branch: branch 1
Leaflet leaf 1
Leaflet leaf 2
Branch: branch 2
Spring 框架也可以使用组合模式来实现对象的层次结构,它提供了一个注解叫做 @Component
,它可以用来标注一个类是一个组件,即一个可被Spring管理的Bean对象。@Component
注解有一个属性叫做value,它可以用来指定组件的名称。我们可以使用 @Component
注解来标注我们的文件类,然后在配置文件或注解中声明这些组件,Spring 就会自动创建和管理这些组件对象。
假设我们有一个文件系统,其中有两种类型的文件:文本文件和文件夹。文本文件是叶子节点,文件夹是组合节点,可以包含其他文件。我们想要使用组合模式来实现文件系统的层次结构,并且提供一个打印文件路径的方法。我们可以使用 @Component
注解来实现,代码如下:
抽象组件不用改造
public interface File {
// 获取文件名称
String getName();
// 添加子文件
void add(File file);
// 删除子文件
void remove(File file);
// 获取子文件
List<File> getChildren();
// 打印文件路径
void printPath();
}
叶子节点添加 @Component("a.txt")
注解
@Component("a.txt")
public class TextFile implements File {
private String name;
public TextFile() {
this.name = "a.txt";
}
@Override
public String getName() {
return name;
}
@Override
public void add(File file) {
throw new UnsupportedOperationException("Text file cannot add child file");
}
@Override
public void remove(File file) {
throw new UnsupportedOperationException("Text file cannot remove child file");
}
@Override
public List<File> getChildren() {
throw new UnsupportedOperationException("Text file has no child file");
}
@Override
public void printPath() {
System.out.println(name);
}
}
组合节点添加 @Component("root")
注解
@Component("root")
public class Folder implements File {
private String name;
private List<File> children;
// 通过@Autowired注解自动注入所有类型为File的Bean对象到children集合中
@Autowired
public Folder(List<File> children) {
this.name = "root";
this.children = children;
}
@Override
public String getName() {
return name;
}
@Override
public void add(File file) {
children.add(file);
}
@Override
public void remove(File file) {
children.remove(file);
}
@Override
public List<File> getChildren() {
return children;
}
@Override
public void printPath() {
System.out.println(name);
for (File child : children) {
child.printPath();
}
}
}
SpringBoot 测试代码
@Slf4j
@SpringBootTest
class SpringBootTest {
@Autowired
private Folder folder;
@Test
void test() {
folder.printPath();
}
}
输出结果:
root
a.txt
组合模式是一种常用的结构型设计模式,它可以将对象组合成树形结构,并且提供了一致的接口来操作单个对象和对象组合,是一种值得学习和掌握的设计模式。
关注公众号【waynblog】每周分享技术干货、开源项目、实战经验、高效开发工具等,您的关注将是我的更新动力!