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

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

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

组合模式是什么?

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

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

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

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

公司下面有部门与人,人是属于部门,部门可以拥有子部门,如果我们将上面的节点,不管是组织,还是人,统一抽象成为一个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】-- 顺便看看享元模式

__EOF__

本文作者秦怀杂货店
本文链接:https://www.cnblogs.com/Damaer/p/15820844.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!

免责声明:本网信息来自于互联网,目的在于传递更多信息,并不代表本网赞同其观点。其原创性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容、文字的真实性、完整性、及时性本站不作任何保证或承诺,并请自行核实相关内容。本站不承担此类作品侵权行为的直接责任及连带责任。如若本网有任何内容侵犯您的权益,请及时联系我们,本站将会在24小时内处理完毕。
相关文章
返回顶部