秦怀杂货店

General Store

  • 首页
  • 文章归档
  • 标签
  • 分类
  • 关于页面

  • 搜索
随便聊聊 数据结构 小游戏 数据库 Docker Springboot 系统设计 雪花算法 分布式 海量ip 最长回文子串 算法 面试题 线程池 多线程 线程 java学习 布隆过滤器 github 架构设计 docsify Git JVM LeetCode 杂货思考 设计模式 Lambda native isAssignableFrom 反射 剑指Offer mybatis SPI JDBC 编程工具 Java基础 集合

设计模式【11】-- 搞定组合模式

发表于 2022-01-19 | 分类于 设计模式 | 0 | 阅读次数 313

设计模式

开局还是那种图,各位客官往下看...

组合模式是什么?

组合模式,将对象组合成树形结构以表示“部分-整体”的层次结构。(百度百科)

其实,组合模式,又称为部分整体模式,用于把一组相似的对象当作一个单一的对象。组合模式依据树形结构来组合对象,用来表示部分以及整体层次。这种类型的设计模式属于结构型模式,它创建了对象组的树形结构。

关键字:一致性,整体,部分

比如公司的组织架构,就是树形的结构:

公司下面有部门与人,人是属于部门,部门可以拥有子部门,如果我们将上面的节点,不管是组织,还是人,统一抽象成为一个node,那么,我们并不需要关心当前节点到底是人,还是部门,统计人数的时候或者遍历的时候,一视同仁。

还有就是Java Swing编程中,一般也会容器的说法:Container,我们在Container里面可以放子的容器,也可以放具体的组件,比如Button或者Checkbox,其实这也是一种部分-整体的思维。

除此之外,最经典的是文件夹与文件的表示,一个文件夹(容器对象)既可以存放文件夹(容器对象),也可以存放文件(叶子对象)。如果把树的每个节点摊平,那就是List。而树结构,则是更能直观的体现每个节点与整体的关系。

为什么需要这个模式呢?它的目的是什么?

主要是想要对外提供一致性的使用方式,即使容器对象与叶子对象之间属性差别可能非常大,我们希望抽象出相同的地方,一致的处理。

组合模式的角色

组合模式中一般有以下三种角色:

  • 抽象构件(Component):一般是接口或者抽象类,是叶子构件和容器构件对象声明接口,抽象出访问以及管理子构件的方法。
  • 叶子节点(Leaf):在组合中表示叶子节点对象,叶子节点没有子节点,也就没有子构件。
  • 容器构件(Composite):容器节点可以包含子节点,子节点可以是叶子节点,也可以是容器节点。

注意:关键点就是抽象构件,所有节点都统一,不再需要调用者关心叶子节点与非叶子节点的差异。

组合模式的两种实现

组合模式有两种不同的实现,分别是透明模式和安全模式:

两者的区别在于透明模式将组合使用的方法放到抽象类中,而安全模式则是放到具体实现类中

透明模式

透明模式是把组合的方法抽象到抽象类中,不管是叶子节点,还是组合节点,都有一样的方法,这样对外处理的时候是一致的,不过实际上有些方法对叶子节点而言,是没有用的,有些累赘。

下面是代码实现:

抽象类,要求实现三个方法,增加,删除,展示:

package designpattern.composite;

public abstract class Component {
    String name;

    public Component(String name) {
        this.name = name;
    }

    public abstract void add(Component component);

    public abstract void remove(Component component);

    public abstract void show(int depth);
}

组合类:

import java.util.ArrayList;
import java.util.List;

public class Composite extends Component {
    List<Component> childs = new ArrayList<>();

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

    @Override
    public void add(Component component) {
        this.childs.add(component);
    }

    @Override
    public void remove(Component component) {
        this.childs.remove(component);
    }

    @Override
    public void show(int depth) {
        for (int i = 0; i < depth; i++) {
            System.out.print("    ");
        }
        System.out.println(name + ": ");
        for (Component component : childs) {
            component.show(depth + 1);
        }
    }
}

叶子类:


public class Leaf extends Component {
    public Leaf(String name) {
        super(name);
    }

    @Override
    public void add(Component component) {

    }

    @Override
    public void remove(Component component) {

    }

    @Override
    public void show(int depth) {
        for (int i = 0; i < depth; i++) {
            System.out.print("    ");
        }
        System.out.println(name);
    }
}

测试类:

public class Test {
    public static void main(String[] args) {
        Composite folderRoot = new Composite("备忘录文件夹");
        folderRoot.add(new Leaf("word 文件"));
        folderRoot.add(new Leaf("ppt 文件"));

        Composite folderLevel1 = new Composite("周报文件夹");
        folderLevel1.add(new Leaf("20210101周报"));
        folderRoot.add(folderLevel1);


        Composite folderLevel2 = new Composite("笔记文件夹");
        folderLevel2.add(new Leaf("jvm.ppt"));
        folderLevel2.add(new Leaf("redis.txt"));
        folderLevel1.add(folderLevel2);


        folderRoot.add(new Leaf("需求.txt"));


        Leaf leaf = new Leaf("bug单.txt");
        folderRoot.add(leaf);
        folderRoot.remove(leaf);

        folderRoot.show(0);
    }
}

运行结果如下:

备忘录文件夹: 
    word 文件
    ppt 文件
    周报文件夹: 
        20210101周报
        笔记文件夹: 
            jvm.ppt
            redis.txt
    需求.txt

可以看到以上是一棵树的结果,不管是叶子节点,还是组合节点,都是一样的操作。

安全模式

安全模式,就是叶子节点和组合节点的特性分开,只有组合节点才有增加和删除操作,而两者都会拥有展示操作。但是如果同时对外暴露叶子节点和组合节点的话,使用起来还需要做特殊的判断。

抽象组件:

public abstract class Component {
    String name;

    public Component(String name) {
        this.name = name;
    }

    public abstract void show(int depth);
}

组件构件:

public class Composite extends Component {
    List<Component> childs = new ArrayList<>();

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

    public void add(Component component) {
        this.childs.add(component);
    }

    public void remove(Component component) {
        this.childs.remove(component);
    }

    @Override
    public void show(int depth) {
        for (int i = 0; i < depth; i++) {
            System.out.print("    ");
        }
        System.out.println(name + ": ");
        for (Component component : childs) {
            component.show(depth + 1);
        }
    }
}

叶子节点:

public class Leaf extends Component {
    public Leaf(String name) {
        super(name);
    }

    @Override
    public void show(int depth) {
        for (int i = 0; i < depth; i++) {
            System.out.print("    ");
        }
        System.out.println(name);
    }
}

测试类不变,测试结果也一样:

备忘录文件夹: 
    word 文件
    ppt 文件
    周报文件夹: 
        20210101周报
        笔记文件夹: 
            jvm.ppt
            redis.txt
    需求.txt

安全模式中,叶子节点没有多余的方法,没有空的方法,外面调用的时候,不会调用到空方法。但是需要对节点进行判断,才能知道哪一个方法能调,哪一个方法不能调。

小结一下

组合模式的优点:

  • 可以分层次定义复杂对象,表示局部和全部,客户端可以忽略不同的节点的差异。
  • 从高层次调用,可以很顺畅的调用到每一个局部,一致性比较强。
  • 节点自由搭配,灵活度比较高。

缺点:

  • 在使用组合模式时,其叶子和组合节点的声明都是实现类,而不是接口,违反了依赖倒置原则。

使用场景:

  • 希望忽略每个部分的差异,客户端一致使用

  • 需要表现为树形结构,以表示“整体-部分”的结构层次。

以一句网友的话结尾:

“张无忌学太极拳,忘记了所有招式,打倒了"玄冥二老",所谓"心中无招"。设计模式可谓招数,如果先学通了各种模式,又忘掉了所有模式而随心所欲,可谓OO之最高境界。”

【作者简介】:
秦怀,公众号【秦怀杂货店】作者,个人网站:http://aphysia.cn,技术之路不在一时,山高水长,纵使缓慢,驰而不息。

剑指Offer全部题解PDF

开源编程笔记

设计模式系列:

  • 设计模式【1】-- 单例模式到底几种写法?
  • 设计模式【1.1】-- 你想如何破坏单例模式?
  • 设计模式【1.2】-- 枚举式单例有那么好用么?
  • 设计模式【1.3】-- 为什么饿汉式单例是线程安全的?
  • 设计模式【2】-- 简单工厂模式了解一下?
  • 设计模式【2.1】-- 简单工厂模式怎么演变成工厂方法模式?
  • 设计模式【2.2】-- 工厂模式怎么演变成抽象工厂模式?
  • 设计模式【3.1】-- 浅谈代理模式之静态、动态、cglib代理
  • 设计模式【3.2】-- JDK动态代理源码分析有多香?
  • 设计模式【3.3】-- CGLIB动态代理源码解读
  • 设计模式【4】-- 建造者模式详解
  • 设计模式【5】-- 原型模式
  • 设计模式【6.1】-- 初探适配器模式
  • 设计模式【6.2】-- 再聊聊适配器模式
  • 设计模式【7】-- 探索一下桥接模式
  • 设计模式【8】-- 手工耿教我写装饰器模式
  • 设计模式【9】-- 外观模式?没那么高大上
  • 设计模式【10】-- 顺便看看享元模式
  • 本文作者: 秦怀杂货店
  • 本文链接: http://aphysia.cn/archives/she-ji-mo-shi-11--gao-ding-zu-he-mo-shi
  • 版权声明: 本博客所有文章除特别声明外,均采用CC BY-NC-SA 3.0 许可协议。转载请注明出处!
# 随便聊聊 # 数据结构 # 小游戏 # 数据库 # Docker # Springboot # 系统设计 # 雪花算法 # 分布式 # 海量ip # 最长回文子串 # 算法 # 面试题 # 线程池 # 多线程 # 线程 # java学习 # 布隆过滤器 # github # 架构设计 # docsify # Git # JVM # LeetCode # 杂货思考 # 设计模式 # Lambda # native # isAssignableFrom # 反射 # 剑指Offer # mybatis # SPI # JDBC # 编程工具 # Java基础 # 集合
2021小结暨2022打脸计划
设计模式【12】-- 搞定最近大火的策略模式
  • 文章目录
  • 站点概览
秦怀杂货店

秦怀杂货店

纵然缓慢,驰而不息。

145 日志
19 分类
37 标签
Github E-mail
Creative Commons
0%
© 2022 秦怀杂货店
由 Halo 强力驱动
|
主题 - NexT.Pisces v5.1.4