组合模式 合成模式 COMPOSITE 结构型 设计模式(十一)

组合模式(合成模式 COMPOSITE)

意图

将对象组合成树形结构以表示“部分-整体”的层次结构。

Composite使得用户对单个对象和组合对象的使用具有一致性。

树形结构介绍

为了便于理解,我们先介绍一下树形结构

什么是树形结构?

windows系统的文件夹树形结构,部门组织架构,行政区...都是一种树形结构

对于最终的节点,称之为叶子;否则是树枝

对于树形结构经常会有一种使用场景:对他们下发一致性的指令

比如:对于操作系统有删除操作,即可以删除一个文件,也可以删除一个文件夹,包括他下面所有的文件

类似删除操作这种命令,并不关心这到底是一个文件(叶子)还是一个文件夹(树枝),关心在意的只是要删除目标

但是对于不同的类型,文件(叶子)还是 文件夹(树枝),他们的处理又的确是不同的

文件只需要删除就可以了,文件夹还需要递归遍历内部的文件夹,直至所有的叶子节点,并且将他们全部删除

这就出现了一个矛盾

客户端其实并不关心到底是叶子还是树枝

但是他却不得不“关心”,因为他需要分情况处理

客户端当然希望不关注目标的具体类型也就是希望能够:满足依赖倒置原则,不关注具体类型,面向抽象进行编程

最简单的方法就是将叶子和树枝抽象出来一种新的类型组件

如此一来,删除操作仅仅关心组件类型,不在关注到底是叶子还是树枝

组件提供统一的协议约定,叶子和树枝共同实现,将它们的不同点的细节封装到他们内部的方法中

这就能够让用户“单个对象和组合对象的使用具有一致性”。

所以,组合模式就是对于树形结构场景下的一种使用模式

共同的抽象提取为新的组件Component,可以表示叶子或者树枝

但是需要注意到:树枝可以有多个树叶组成,树枝上面也可能是树枝,也就是说,作为树枝的节点,也会包含Component

所以完整的结构图为:

所以组合模式的意图,从结构图中的Component中就可以看出来

他们都是Component,所以具有一致性。

借助于Composite与Component的关系,又能够表述整体与部分的关系。

结构

Component 抽象构建角色

根据单个对象和组合对象的特点,规定的一个抽象角色(接口或抽象类),定义了共同的行为

或者说将"整体"和"部分"提取共性,进行抽象提取。

Leaf 叶子角色 参与组合对象的单个对象,也就是定义了参加组合对象的原始根本对象的行为 叶子节点下没有下级对象

Composite组合对象角色

也就是树枝角色,单个对象组合起来的一个对象,由多个单一对象构成

并且给出组合对象的行为(实现Component约定的行为)

Client客户端角色

给单个对象或者组合对象施加命令,也就是调用Component中的方法,比如删除行为

示例代码

以删除文件为例

FileSystem文件系统类 拥有删除方法delete() 

他有两个实现类文件 File 和文件夹Folder

Folder中可以有文件和文件夹,使用内部的List<FileSystem>保存  

File的delete方法直接删除,Folder则会便利内部的List<FileSystem>  逐个删除

package composite;
public interface FileSystem {
void delete();
}
package composite;
public class File implements FileSystem {
@Override
public void delete() {
System.out.println("delete file...");
}
}
package composite;
import java.util.ArrayList;
import java.util.List;
 
public class Folder implements FileSystem {
    List<FileSystem> fileSystemList = new ArrayList<>();
    @Override
    public void delete() {
        for(FileSystem fileSystem:fileSystemList){
            fileSystem.delete();
        }
    }
    public void add(FileSystem fileSystem){
        fileSystemList.add(fileSystem);
    }
}

客户端

package composite;
public class Client {
    public static void del(FileSystem fileSystem){
        fileSystem.delete();
        System.out.println("DELETED");
        System.out.println();
    }
 
    public static void main(String[] args){
        Folder folder = new Folder();
         
        Folder folder1 = new Folder();
        Folder folder2 = new Folder();
        Folder folder3 = new Folder();
         
        File file1 = new File();
        File file2 = new File();
        File file3 = new File();
        File file4 = new File();
         
        folder.add(file1);
        folder.add(folder1);
        folder.add(folder2);
         
        folder1.add(file2);
        folder1.add(folder3);
        folder3.add(file4);
         
        folder2.add(file3);
        
        del(folder);
        del(folder1);
        del(file2);
        del(folder2);
    }
}

客户端Client中    del(FileSystem fileSystem) 方法用于对整个组件进行命令下达

内部调用组件FileSystem的delete方法

通过文件夹Folder的add方法我们构建了下面这种形式的树形结构

通过下面代码进行测试

        del(folder);

        del(folder1);

        del(file2);

        del(folder2);

在示例代码中,借助于FileSystem这一抽象的组件Componet

将File 这一Leaf角色和 Folder 这一Composite角色  组织成 “部分--整体”的树形结构

并且,对于客户端提供统一的外在形式----Component

使得客户端对单个对象和组合对象的使用具有一致性

这就是组合模式的运用

两种形式

如果你有留意,可以看得到前面的代码示例中

FileSystem中仅仅只有一个delete方法

File也实现了这一个方法,但是Folder 中却有了add方法

也就是说,Composite角色中有与Component中不同的方法!

树枝中可以有树枝或者叶子节点,也就是组合对象中可以包含组合对象或者单一对象

那么,也就是说:组合对象要提供子对象的管理方法,比如上面的add  可能还会有remove等

上面的例子中,我们将add方法安置于Composite 中

这被称为安全方式的合成模式

因为是在Composite中管理子对象,叶子节点类型的对象根本就没有这些方法,所以也不能对客户端执行这些方法

但是,叶子节点和树枝节点不够透明,他们拥有不同的方法

另外一种是将子对象的管理全部托管在Component中

也就是叶子节点和树枝节点都将拥有这些方法,方法都是一样的,对客户端来说,叶子和树枝在方法接口层面上的区别没有了

客户端可以完全同等的对待它们两者,这就是透明方式的合成模式

但是,它不够安全,因为叶子节点和树枝节点逻辑上本来就是不相同的

叶子节点也不会有下一级子节点,所以这些方法没有意义,而且如果使用编译期间也不会报错,会把问题留到运行中

两种方式中,透明就不够安全,安全就不透明,所以根据实际情况按照需求进行选择

总结

组合模式的根本在于抽象组件,对于具有整体与部分关系的事物,如果需要一致性的外在表现,就可以提取共性进行抽象,这就是组合模式。

将相关联的对象组织成“部分--整体”的树形结构形式,通过抽象构建,所有的节点都是Component

对于客户端来说,不管到底是Leaf还是Composite,他们都是Component

高层模块并不需要关心,处理的到底是单个对象还是组合的对象

只要是比较符合“部分--整体”关系,或者说是树形结构,以及当你希望用户可以忽略组合对象和单个对象的区别时,那么就可以考虑使用组合模式

当增加新类型组件时,新定义的Composite或者Leaf子类自动的与已有的结构和客户代码一起工作

客户端程序不需要因此而变化,从这个角度看,符合开闭原则。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏编程

养良好C语言编程风格,编优质C语言代码,这才是C语言的开始

个人c语言编程风格总结 总结一下我个人的编程风格及这样做的原因吧,其实是为了给实验室写一个统一的C语言编程规范才写的。首先声明,我下面提到的编程规范,是自己给自...

5395
来自专栏Java爬坑系列

Java强引用、软引用、弱引用及虚引用深入探讨

引用类型在日常开发中并不常关注,也很少注意到,因此很多人忽略了它们的存在,而事实上,引用类型在Java体系中扮演着十分重要的角色,要想对Java体系有一个更深层...

1152
来自专栏余林丰

组合模式

在之前有接触过组合模式,那是第一次接触设计模式,觉得什么是组合模式啊?什么部分与整体。现在再来看组合模式,觉得实际上并没有那么神秘。 组合模式:将对象组合成树形...

2065
来自专栏大内老A

WCF技术剖析之二十二: 深入剖析WCF底层异常处理框架实现原理[中篇]

在[上篇]中,我们分别站在消息交换和编程的角度介绍了SOAP Fault和FaultException异常。在服务执行过程中,我们手工抛出FaultExcept...

24510
来自专栏Web 开发

JavaScript Cookbook 2nd 之 Function

昨晚翻了一下,虽然都是一些旧知识,不过深入下去对照着其他资料一起看,还是能发现一些有意思的地方。

940
来自专栏从流域到海域

《Java程序设计基础》 第4章手记

《Java程序设计基础》 第4章手记 本章主要内容 - 语句和复合语句 - 分支结构 - 循环结构 - 跳转语句 这四部...

2068
来自专栏ml

C++ template的一些高级用法(元编码,可变参数,仿函数,using使用方法,. C++ 智能指针)

1 .  通用函数可变参数模板      对于有些时候,我们无法确切的知道,函数的参数个数时,而又不想过多的使用所谓的函数重载,那么就可以效仿下面的例子: 1...

8664
来自专栏Java帮帮-微信公众号-技术文章全总结

Java代码效率优化【面试+提高】

JAVA代码效率优化 最近在想自己编程时是否注意过代码的效率问题,得出的答案是:没有。代码只是实现了功能,至于效率高不高没怎么关注,这应该是JAVA程序员进阶...

46013
来自专栏java一日一条

优秀Java程序员必须了解的GC工作原理

一个优秀的Java程序员必须了解GC的工作原理、如何优化GC的性能、如何与GC进行有限的交互,因为有一些应用程序对性能要求较高,例如嵌入式系统、实时系统等,只有...

1674
来自专栏Python中文社区

Python中的协议 、鸭子类型 、 抽象基类 、混入类

本篇文章探讨一下python中的几个概念:协议 、鸭子类型 、 抽象基类 、混入类。

1572

扫码关注云+社区

领取腾讯云代金券