组合模式在我们的生活场景中也有体现,比如说我们的公司组织结构,大的部门中间包含一个个一级部门,一级部门下还有不同的子部门。 或者说我们计算机中的文件系统,根目录下有各文件,文件夹下可以有子文件夹,也可以有文件。子文件夹下也可以有文件夹或者文件。类似于这种树形结构的业务架构,今天所说的组合模式能可好的解决此类问题。
组合(Composite Pattern)模式的定义:将一组对象组织(Compose)成树形结构,以表示一种“部分-整体”的层次结构。使用户对单个对象和组合对象具有一致的访问性。
代码案例:
通过模拟文件系统,体会一下组合模式的使用;
public abstract class FileSystemComponent {
protected String path;
public FileSystemComponent(String path) {
this.path = path;
}
// 文件数量
public abstract int countNumOfFiles();
// 文件内容大小
public abstract Long countSizeOfFiles();
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
public abstract String show();
}
public class FileLeaf extends FileSystemComponent{
public FileLeaf(String path) {
super(path);
}
@Override
public int countNumOfFiles() {
return 0;
}
@Override
public Long countSizeOfFiles() {
return 0L;
}
@Override
public String show() {
return "/"+path;
}
}
public class DirectoryComposite extends FileSystemComponent {
private List<FileSystemComponent> subFileStemComponent = new ArrayList<FileSystemComponent>();
public DirectoryComposite(String path) {
super(path);
}
@Override
public int countNumOfFiles() {
int numOfFiles = 0;
for (FileSystemComponent fileLeaf : subFileStemComponent) {
numOfFiles += fileLeaf.countNumOfFiles();
}
return numOfFiles;
}
@Override
public Long countSizeOfFiles() {
Long sizeOfFiles = 0L;
for (FileSystemComponent fileSystemComponent : subFileStemComponent) {
sizeOfFiles += fileSystemComponent.countSizeOfFiles();
}
return sizeOfFiles;
}
public void addSubNode(FileSystemComponent fileSystemComponent) {
subFileStemComponent.add(fileSystemComponent);
}
public void removeSubNode(FileSystemComponent fileSystemComponent) {
int size = subFileStemComponent.size();
//循环当前文件夹下的所有目录
int i =0;
for (; i < size; i++) {
// 如果文件名相同 获取当前的坐标
if (subFileStemComponent.get(i).getPath().equalsIgnoreCase(fileSystemComponent.getPath())) {
break;
}
}
//根据坐标删除文件
subFileStemComponent.remove(i);
}
@Override
public String show() {
for (Object fileSystemComponent : subFileStemComponent) {
System.out.println(path + ((FileSystemComponent)fileSystemComponent).show());
}
return "";
}
}
public class Client {
public static void main(String[] args) {
// 模拟一下树形结构的管理
// |- Desktop
// |---------dir
// |-------------test.txt
// |-------------tmp
// |----------------java.word
// 文件1
FileSystemComponent text = new FileLeaf("test.txt");
// 文件夹1
DirectoryComposite dir = new DirectoryComposite("Desktop/dir");
// 文件夹 Desktop/dir 下新增一个文件
dir.addSubNode(text);
// 文件夹2 Desktop/dir 下新增一个文件夹
DirectoryComposite tmp = new DirectoryComposite("Desktop/dir/tmp");
// 文件2
FileSystemComponent test = new FileLeaf("java.word");
tmp.addSubNode(test);
// 文件夹 Desktop/dir 添加到文件夹 Desktop/dir 下
dir.addSubNode(tmp);
dir.show();
}
}
执行结果:
Desktop/dir/test.txt
Desktop/dir/tmp/java.word
Desktop/dir
优点:
缺点:
其实组合模式更偏向于具体某一种场景的使用,也可以说是针对于树形这种结构的场景。所以它解决的问题也比较单一。
1.组合模式在HashMap中的使用
先看一段案例代码
public class JDKUsage {
public static void main(String[] args) {
Map<String, Integer> map = new HashMap<String, Integer>();
map.put("a", 1);
map.put("b", 2);
Map<String, Integer> map1 = new HashMap<String, Integer>();
map1.put("c", 3);
map.putAll(map1);
System.out.println(map);
}
}
执行结果:{a=1, b=2, c=3}
不用多讲,都会使用Map的Api,那它底层是如何实现的,为什么说这里使用到了组合模式。
首先看下Hash的层次结构:
public class HashMap<K,V> extends AbstractMap<K,V>
implements Map<K,V>, Cloneable, Serializable {
}
这里的Map
就是抽象构建角色,包含put、putAll
等方法,而HashMap
则为中间构件角色,所有的数据操作都是在Node
节点,静态内部类的数组 Node
为叶子节点角色。
// node节点
static class Node<K, V> implements Map.Entry<K, V> {
final int hash;
final K key;
V value;
Node<K, V> next;
Node(int hash, K key, V value, Node<K, V> next) {
this.hash = hash;
this.key = key;
this.value = value;
this.next = next;
}
//略
// 往节点set值 put、putAll最终都会调此方法
final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict) {
Node<K, V>[] tab;
Node<K, V> p;
int n, i;
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length;
if ((p = tab[i = (n - 1) & hash]) == null)
tab[i] = newNode(hash, key, value, null);
else {
Node<K, V> e;
K k;
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
e = p;
else if (p instanceof TreeNode)
e = ((TreeNode<K, V>) p).putTreeVal(this, tab, hash, key, value);
else {
for (int binCount = 0; ; ++binCount) {
if ((e = p.next) == null) {
p.next = newNode(hash, key, value, null);
if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
treeifyBin(tab, hash);
break;
}
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
break;
p = e;
}
}
if (e != null) {
// existing mapping for key
V oldValue = e.value;
if (!onlyIfAbsent || oldValue == null)
e.value = value;
afterNodeAccess(e);
return oldValue;
}
}
++modCount;
if (++size > threshold)
resize();
afterNodeInsertion(evict);
return null;
}
//略
}
从HashMap案例中,可以看到设计模式其实提供的是一种软件设计思想,并没有完全安装标准的概念来。许多优秀的框架,也是基于好的设计模式,灵活设计模块的组件。
✨✨ 欢迎订阅个人的微信公众号 享及时博文更新