[设计模式] - 组合模式

一、组合模式的简介

1. 什么是组合模式

组合模式(composite Pattern),是结构向设计模式的一种,也叫做部分整体模式。这个模式是为了解决展现一组相似对象的层级结构的问题,将对象组合成树形结构以表示部分-整体的层侧结构。组合模式使得用户对单个对相爱那个和组合对象的使用具有一致性。

2. 组合模式的使用场景

通常组合模式应用在两种场景:

  1. 当你想要表现对象的部分-整体层次结构(树形结构)
  2. 你希望用户忽略整体对象与单个对象的不同,用户将统一的使用组合结构中的所有对象

二、通过业务场景讲述组合模式

1. 提出一个使用场景

举个简单的例子,其实我们电脑的文件系统就是一个非常典型的树形结构。比如说我们想要保存文件的时候,操作系统会让我们选择一个保存路径,那么我们简单的分析一下这个路径,是不是一个很明显的层级关系。
在这里插入图片描述

首先在这里,D:表示的是我们想要存储的目标磁盘,Users\动图则是我们想要保存的路径,这个路径是由多个文件夹组成,而每一级文件夹下可能并不只有一个下级文件夹,就比如我们打开动图文件夹可以看到:
在这里插入图片描述
也就是说每一个文件夹下可能还有多个下级文件夹,这里我们通过一个结构图来说明下:
在这里插入图片描述

假定我们电脑中D盘的文件系统结构如下,那么我想创建一个简单的模型来展示这个系统结构我该怎么做?

2. 业务分析

首先我们来分析下这个文件系统的一个业务属性。首先,不管是磁盘D还是下级的文件夹目录,都拥有维护文件的功能和属性,简单来说就是都可以对文件作出增删改查相关操作。其次他们都拥有某些固有属性,比如名称,大小等。那我们可不可以将这些共有属性和行为抽离,来达到功能复用及方便拓展维护的目的呢?当然是可以的。这里我们提到的将所有共性的部分抽离,就是组合模式的第一个组成角色抽象根节点(component)。component是组合模式中的对象声明接口,它主要是负责定义所有类拥有的共有行为和属性,用于方便业务方访问和管理子类。有了抽象根节点,我们接下来需要分析的是各级文件夹的功能,他们主要是用来存储文件,如果我们将整个文件系统比喻成一棵树,那么每一级文件夹就是这棵树的树枝,而每一层文件夹下保存的文件就是这棵树的树叶。那么树枝在组合系统扮演的角色就是树枝节点(composite),composite主要就是用来存放子节点。而整个文件系统中最后一层的各个格式的文件,其本身并不具备存放节点的能力,因此它就像一棵树的叶子一样,扮演着**叶子节点(leaf)**的角色,leaf下面没有其他的分支,是组合模式系统层级的最小单位。那么接下来我们将系统结构图按照我们分析的模式转换一下,可以看到:
在这里插入图片描述
这个结构就是组合模式的基本结构。当然在结构的基础上,针对与业务的抽离度,组合模式的实现也分为两种。

三、组合模式的分类及实现

组合模式通常来说有两种实现:透明模式和安全模式,两种模式的主要区别就是透明模式会把组合的所有共同行为统一放入component中,让不同层次的节点都拥有一致的行为,这样做的好处就是客户端不需要分辨他是那一层级的节点就可以统一使用,但是缺点很明显,叶子节点会继承到对他来说无用的行为。因此安全模式就是为了解决这个问题,在安全模式中,component只会抽离基础行为,而枝节点和叶子节点本身的方法需要放在自身中实现。

1. 透明组合模式的实现

话不多说,我们采用透明组合模式来实现一个文件系统层级的遍历功能;

(1) 创建component

public abstract class FileComponent {
    
    

	protected String name;

	// 限制文件创建时 必须拥有自己的名字
	public FileComponent(String name) {
    
    
		super();
		this.name = name;
	}

	// 允许获取名称
	public String getName() {
    
    
		return name;
	}

	// 允许修改名称
	public void setName(String name) {
    
    
		this.name = name;
	}

	// 添加下级节点
	public void add(FileComponent fileComponent) {
    
    
		throw new UnsupportedOperationException("暂不支持该方法");
	}

	// 查询
	public abstract void print();

}

(2) 创建composite

public class FileComposite extends FileComponent {
    
    
	private List<FileComponent> FileComposite = new ArrayList<FileComponent>();

	public FileComposite(String name) {
    
    
		super(name);
	}

	@Override
	public void add(FileComponent fileComponent) {
    
    
		FileComposite.add(fileComponent);
	}

	@Override
	public void print() {
    
    
		System.out.println(getName());
		for (FileComponent fileComponent : FileComposite) {
    
    
			System.out.print("  -");
			fileComponent.print();
		}
	}

}

(3) 创建leaf

public class FileLeaf extends FileComponent {
    
    

	public FileLeaf(String name) {
    
    
		super(name);

	}

	@Override
	public void print() {
    
    
		System.out.println("-" + getName());
		System.out.println("-------------");

	}

}

(4) 创建测试用例及测试结果

public class Test {
    
    

	public static void main(String[] args) {
    
    
		FileComponent fileComposite = new FileComposite("D:");
		FileComponent users = new FileComposite("Users");
		FileComponent game = new FileComposite("game");
		fileComposite.add(users);
		fileComposite.add(game);
		FileComponent gif = new FileComposite("动图");
		users.add(gif);
		FileComponent fileLeaf = new FileLeaf("组合模式.gif");
		gif.add(fileLeaf);
		fileComposite.print();
	}
}

测试结果:
在这里插入图片描述
这里我们观察到一个文件层级的遍历系统已经设计完毕。 这里的新增文件方法是被我们抽离到了Component中实现的,显然这个方法是不被叶子节点需要的,因此我们可以进一步对这个代码做个小优化,将叶子节点不需要的功能放到composite各自中去实现。

2. 安全组合模式的实现

(1) 创建Component

public abstract class FileComponent {
    
    

	protected String name;

	public FileComponent(String name) {
    
    
		super();
		this.name = name;
	}

	public String getName() {
    
    
		return name;
	}

	public void setName(String name) {
    
    
		this.name = name;
	}

	public abstract void print();
}

(2) 创建Composite

public class FileComposite extends FileComponent {
    
    

	private List<FileComponent> FileComposite = new ArrayList<FileComponent>();

	public FileComposite(String name) {
    
    
		super(name);
		// TODO Auto-generated constructor stub
	}

	@Override
	public void print() {
    
    
		System.out.println(getName());
		for (FileComponent fileComponent : FileComposite) {
    
    
			System.out.print("  -");
			fileComponent.print();
		}
	}

	public void add(FileComponent fileComponent) {
    
    
		FileComposite.add(fileComponent);
	}

}

(3) 创建leaf

public class FileLeaf extends FileComponent {
    
    

	public FileLeaf(String name) {
    
    
		super(name);
	}

	@Override
	public void print() {
    
    
		System.out.println("-" + getName());
		System.out.println("-------------");
	}

}

(4) 创建测试用例及测试结果

public class Test {
    
    

	public static void main(String[] args) {
    
    
		FileComposite fileComposite = new FileComposite("D:");
		FileComposite users = new FileComposite("Users");
		FileComposite game = new FileComposite("game");
		fileComposite.add(users);
		fileComposite.add(game);
		FileComposite gif = new FileComposite("动图");
		users.add(gif);
		FileComponent fileLeaf = new FileLeaf("组合模式.gif");
		gif.add(fileLeaf);
		fileComposite.print();
	}
}

测试结果:
在这里插入图片描述

四、总结组合模式

1. 组合模式的构成

组合模式分为三个角色组成:

  1. 抽象根节点(compoent):负责定义系统各层级的共有属性及方法,也可以预实现一些默认行为和属性。
  2. 树枝节点(composite):负责存储子节点,拥有系统的共有属性和行为。
  3. 叶子节点(leaf):叶子节点,其下面再无分支,是系统层级的最小单位。

2. 组合模式的结构图

如图:
在这里插入图片描述

3. 组合模式的优缺点

优点:

  1. 组合模式抽离了系统的公有属性,隐藏了对象之间的差异,使得业务系统在使用时可以忽略差异,使用一致性的行为控制不同层级。
  2. 可以很方便的添加下级节点,对现有类库并没有侵入,符合开闭原则。

缺点:

  1. 如果树结构过于庞大,系统的深度遍历可能会导致运行时间过长。
  2. 组合模式违背了依赖倒置原则

4. 组合模式在JDK中的使用

JDK中的Map类的实现就引用了组合模式,感兴趣的同学可以自行翻阅源码及材料查看,这里就不过多赘述了。

好了,今天的内容到此结束,如果还有疑问的同学可以私信我或在评论区留言,我会在第一时间为你解答。觉得有收获的小伙伴请记得一键三连,关注博主不要走丢,拒当白嫖党~

猜你喜欢

转载自blog.csdn.net/xiaoai1994/article/details/112917978