设计模式系列之组合模式及其在JDK和MyBatis源码中的运用详解

组合模式及其在JDK源码中的运用 前言组合和聚合什么是组合模式示例透明组合模式透明组合模式的缺陷安全组合模式 组合模式角色组合模式在JDK源码中的体现组合模式应用场景享元模式优缺点总结

前言

本文主要会讲述组合模式的用法,并会结合在JDK和MyBatis源码中的运用来进一步理解组合模式。

在编码原则中,有一条是:多用组合,少用继承。当然这里的组合和我们今天要讲的组合模式并不等价,这里的组合其实就是一种聚合,那么聚合和组合有什么区别呢?

组合和聚合

人在一起叫团伙,心在一起叫团队。用这句话来诠释组合与聚合的区别是相对恰当的。

聚合就是说各个对象聚合在一起工作,但是我没有你也行,我照样可以正常运行。但是组合呢,关系就比较密切,组合中的各个对象之间组成了一个整体,缺少了某一个对象就不能正常运行或者说功能会有很大缺陷。
也就是说聚合对象不具备相同生命周期,而组合的对象具有相同的生命周期

举个例子:
比如说电脑和U盘就是聚合,而电脑显示器和主机就是组合。

什么是组合模式

组合模式(Composite Pattern)也称之为整体-部分(Part-Whole)模式。组合模式的核心是通过将单个对象(叶子节点)和组合对象(树枝节点)用相同的接口进行表示,使得单个对象和组合对象的使用具有一致性。组合模式属于结构型模式。

组合模式一般用来描述整体与部分的关系,它将对象组织到树形结构中,最顶层的节点称为根节点,根节点下面可以包含树枝节点和叶子节点,树枝节点下面又可以包含树枝节点和叶子节点如下图所示:

讲了这么多,感觉有点抽象,所以依然是老规矩:Talk is cheap,Show you the code

示例

组合模式有两种写法,分别是透明模式安全模式。下面我们就以高考的科目为例来看看组合模式是如何体现在代码中的

透明组合模式

1、首先建立一个顶层的抽象科目类,这个类中定义了三个通用操作方法,但是均默认不支持操作

package com.zwx.design.pattern.composite.transparency;

/**
 * 顶层抽象组件
 */
public abstract class GkAbstractCourse {
  public void addChild(GkAbstractCourse course){
    System.out.println("不支持添加操作");
  }

  public String getName() throws Exception {
    throw new Exception("不支持获取名称");
  }

  public void info() throws Exception{
    throw new Exception("不支持查询信息操作");
  }
}

PS:这个类中的公共方法之所以不定义为抽象方法的原因是因为假如定义为抽象方法,那么所有的子类都必须重写父类方法,这样体现不出差异性。而这种通过抛异常的方式,如果子类需要用到的功能就重写覆盖父类方法即可。

2、新建一个普通科目类继承通用科目抽象类,这个类作为叶子节点,没有重写addChild方法,也就是这个类属于叶子节点,不支持添加子节点:

package com.zwx.design.pattern.composite.transparency;

/**
 * 普通科目类(叶子节点)
 */
public class CommonCource extends GkAbstractCourse {
  private String name;//课程名称
  private String score;//课程分数

  public CommonCource(String name, String score) {
    this.name = name;
    this.score = score;
  }

  @Override
  public String getName(){
    return this.name;
  }

  @Override
  public void info() {
    System.out.println("课程:" + this.name + ",分数:" + score);
  }
}

3、建立一个具有层级的节点,三个方法都重写了,支持添加子节点,这个类里面为了方便打印的时候看出层级关系,所以我定义了一个层级属性。

package com.zwx.design.pattern.composite.transparency;

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

/**
 * 树枝节点
 */
public class LevelCource extends GkAbstractCourse{
  private List<GkAbstractCourse> courseList = new ArrayList<>();
  private String name;
  private int level;

  public LevelCource(String name, int level) {
    this.name = name;
    this.level = level;
  }

  @Override
  public void addChild(GkAbstractCourse course) {
    courseList.add(course);
  }

  @Override
  public String getName(){
    return this.name;
  }

  @Override
  public void info() throws Exception {
    System.out.println("课程:" + this.name);
    for (GkAbstractCourse course : courseList){
      for (int i=0;i<level;i++){
        System.out.print(" ");
      }
      System.out.print(">");
      course.info();
    }
  }
}

4、建立一个测试类来测试一下:

package com.zwx.design.pattern.composite.transparency;

public class TestTransparency {
  public static void main(String[] args) throws Exception {
    GkAbstractCourse ywCourse = new CommonCource("语文","150");
    GkAbstractCourse sxCourse = new CommonCource("数学","150");
    GkAbstractCourse yyCourse = new CommonCource("英语","150");

    GkAbstractCourse wlCourse = new CommonCource("物理","110");
    GkAbstractCourse hxCourse = new CommonCource("化学","100");
    GkAbstractCourse swCourse = new CommonCource("生物","90");

    GkAbstractCourse lzCourse = new LevelCource("理综",2);
    lzCourse.addChild(wlCourse);
    lzCourse.addChild(hxCourse);
    lzCourse.addChild(swCourse);

    GkAbstractCourse gkCourse = new LevelCource("理科高考科目",1);
    gkCourse.addChild(ywCourse);
    gkCourse.addChild(sxCourse);
    gkCourse.addChild(yyCourse);
    gkCourse.addChild(lzCourse);

    gkCourse.info();
  }
}

输出结果:

课程:理科高考科目
  >课程:语文,分数:150
  >课程:数学,分数:150
  >课程:英语,分数:150
  >课程:理综
    >课程:物理,分数:110
    >课程:化学,分数:100
    >课程:生物,分数:90

这里如果用普通科目去调用add方法就会抛出异常,假如上面调用:

swCourse.addChild(ywCourse);

会输出

不支持添加操作

因为在普通科目类里面并没有重写addChild方法。

透明组合模式的缺陷

透明模式的特点就是将组合对象所有的公共方法都定义在了抽象组件内,这样做的好处是客户端无需分辨当前对象是属于树枝节点还是叶子节点,因为它们具备了完全一致的接口,不过缺点就是叶子节点得到到了一些不属于它的方法,比如上面的addChild方法,这违背了接口隔离性原则

安全组合模式

安全组合模式只是规定了系统各个层次的最基础的一致性行为,而把组合(树节点)本身的方法(如树枝节点管理子类的addChild等方法)放到自身当中。

1、首先还是建立一个顶层的抽象根节点(这里面只定义了一个通用的抽象info方法):

package com.zwx.design.pattern.composite.safe;

package com.zwx.design.pattern.composite.safe;

/**
 * 顶层抽象组件
 */
public abstract class GkAbstractCourse {
  protected String name;
  protected String score;

  public GkAbstractCourse(String name, String score) {
    this.name = name;
    this.score = score;
  }

  public abstract void info();
}

2、建立一个叶子节点(这里只是重写了info方法,没有定义其他特有方法):

package com.zwx.design.pattern.composite.safe;

/**
 * 叶子节点
 */
public class CommonCource extends GkAbstractCourse {

  public CommonCource(String name,String score) {
    super(name,score);
  }

  @Override
  public void info() {
    System.out.println("课程:" + this.name + ",分数:" + this.score);
  }
}

3、定义一个树枝节点(这个类当中定义了一个树枝特有的方法addChild):

package com.zwx.design.pattern.composite.safe;

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

/**
 * 树枝节点
 */
public class LevelCource extends GkAbstractCourse{
  private List<GkAbstractCourse> courseList = new ArrayList<>();
  private int level;

  public LevelCource(String name, String score,int level) {
    super(name,score);
    this.level = level;
  }

  public void addChild(GkAbstractCourse course) {
    courseList.add(course);
  }

  @Override
  public void info() {
    System.out.println("课程:" + this.name + ",分数:" + this.score);
    for (GkAbstractCourse course : courseList){
      for (int i=0;i<level;i++){
        System.out.print(" ");
      }
      System.out.print(">");
      course.info();
    }
  }
}

4、新建测试类来测试:

package com.zwx.design.pattern.composite.safe;

public class TestSafe {
  public static void main(String[] args) throws Exception {
    CommonCource ywCourse = new CommonCource("语文","150");
    CommonCource sxCourse = new CommonCource("数学","150");
    CommonCource yyCourse = new CommonCource("英语","150");

    CommonCource wlCourse = new CommonCource("物理","110");
    CommonCource hxCourse = new CommonCource("化学","100");
    CommonCource swCourse = new CommonCource("生物","90");

    LevelCource lzCourse = new LevelCource("理综","300",2);
    lzCourse.addChild(wlCourse);
    lzCourse.addChild(hxCourse);
    lzCourse.addChild(swCourse);

    LevelCource gkCourse = new LevelCource("理科高考","750",1);
    gkCourse.addChild(ywCourse);
    gkCourse.addChild(sxCourse);
    gkCourse.addChild(yyCourse);

    gkCourse.addChild(lzCourse);
    gkCourse.info();
  }
}

输出结果为:

课程:理科高考,分数:750
  >课程:语文,分数:150
  >课程:数学,分数:150
  >课程:英语,分数:150
  >课程:理综,分数:300
    >课程:物理,分数:110
    >课程:化学,分数:100
    >课程:生物,分数:90

这里和透明方式不一样,叶子节点不具备addChild功能,所以无法调用,而上面的示例中时可以被调用,但是调用之后显示不支持,这就是这两种写法最大的区别。

组合模式角色

从上面示例中,可以看到组合模式包含了以下三个角色:

  • 抽象根节点(Component):定义系统各层次对象的公有属性和方法,可以预先定义一些默认行为和属性。
  • 树枝节点(Composite):定义树枝节点的行为,存储子节点,组合树枝节点和叶子节点形成一个树形结构。
  • 叶子节点(Leaf):是系统遍历层次中的最小单位,下面没有子节点。

组合模式在JDK源码中的体现

1、HashMap

HashMap中有一个putAll方法,参数是一个Map,这就是一种组合模式的体现:

另外还有ArrayList中的addAll方法也是一样。

2、MyBatis中有一个SqlNode接口,下面很多一级标签:

然后一级标签下面又有二级标签(这就是组合模式的体现):

组合模式应用场景

组合模式一般应用在有层级关系的场景,最经典的就是树形菜单,文件和文件夹的管理等

享元模式优缺点

优点:清楚的定义了分层次的复杂对象,让客户端可以忽略层次的差异,方便对整个层次进行动态控制。
缺点:其叶子和树枝的声明是实现类而不是接口,违反了依赖倒置原则,而且组合模式会使设计更加抽象不好理解。

总结

本文主要介绍了组合模式,并介绍了普通的聚合和组合之间的区别,并通过例子详细解释了组合模式中的透明写法和安全写法的区别,最后结合在JDK和MyBatis源码中的运用来进一步理解组合模式的运用。
请关注我,和孤狼一起学习进步

到此这篇关于设计模式系列之组合模式及其在JDK和MyBatis源码中的运用详解的文章就介绍到这了,更多相关组合模式在JDK和MyBatis源码中的运用内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 23种设计模式(10)java组合模式

    23种设计模式第四篇:java组合模式 介绍 组合模式又叫做部分-整体模式,它使我们树型结构的问题中,模糊了简单元素和复杂元素的概念,客户程序可以向处理简单元素一样来处理复杂元素,从而使得客户程序与复杂元素的内部结构解藕. 组合模式可以优化处理递归或分级数据结构.有许多关于分级数据结构的例子,使得组合模式非常有用武之地. 类图 组成部分: Component: 为参加组合的对象声明一个公共接口, 不管是组合还是叶结点. Leaf: 在组合中表示叶子结点对象,叶子结点没有子结点. Composit

  • Java使用组合模式实现表示公司组织结构功能示例

    本文实例讲述了Java使用组合模式实现表示公司组织结构功能.分享给大家供大家参考,具体如下: 一.模式定义 组合模式:将对象组合成树形结构以表示"部分一整体"的层次结构,组合模式使得用户对单个对象和组合对象的使用具有一致性. 二.组合模式举例 1 模式分析 我们借用公司组织结构图来说明这一模式. 经过分析后,我们得出该模式静态类图如下: 2 代码示例 2.1 建立员工抽象类 package com.demo.composite; /** * 职工类接口 * * @author * */

  • 轻松掌握java组合模式

    组合模式,将对象组合成树形结构以表示"部分-整体"的层次结构,组合模式使得用户对单个对象和组合对象的使用具有一致性,组合模式可以让客户端像修改配置文件一样简单的完成本来需要流程控制语句来完成的功能. 特点:对于递归或者类似树形的分级数据结构,可以用最简单的方式进行处理. 企业级开发和常用框架中的应用:系统目录结构和网站导航结构 下面以目录结构举例: 场景:假设我们现在有一个目录,目录下面还有子目录和文件,现在我们要查看整个目录及目录下的所有文件和创建时间 具体代码如下: package

  • Java设计模式之组合模式(Composite模式)介绍

    Composite定义:将对象以树形结构组织起来,以达成"部分-整体" 的层次结构,使得客户端对单个对象和组合对象的使用具有一致性. Composite比较容易理解,想到Composite就应该想到树形结构图.组合体内这些对象都有共同接口,当组合体一个对象的方法被调用执行时,Composite将遍历(Iterator)整个树形结构,寻找同样包含这个方法的对象并实现调用执行.可以用牵一动百来形容. 所以Composite模式使用到Iterator模式,和Chain of Responsi

  • 快速理解Java设计模式中的组合模式

    组合模式是一种常见的设计模式(但我感觉有点复杂)也叫合成模式,有时又叫做部分-整体模式,主要是用来描述部分与整体的关系. 个人理解:组合模式就是将部分组装成整体. 定义如下: 将对象组合成树形结构以表示"部分-整体"的层次结构,使得用户对单个对象和组合对象的使用具有一致性. 通用类图如下: 组合模式的包含角色: ● Component 抽象构件角色 定义参加组合对象的共有方法和属性,可以定义一些默认的行为或属性. ● Leaf 叶子构件 叶子对象,其下再也没有其他的分支,也就是遍历的最

  • JAVA设计模式之组合模式原理与用法详解

    本文实例讲述了JAVA设计模式之组合模式.分享给大家供大家参考,具体如下: 组合(整体与部分关系)模式:将不同但是相关的对象组合成树形结构以实现"部分-整体"的层次结构,使得用户对单个对象和组合对象的使用具有一致性. * 模式角色组成: 1.Component对象: 是组合中的对象接口,是所有类共有的接口.是用于统一定义整体中的部分. 2.Leaf对象: 整体中的部分,没有下一级. 3.Composite对象: 用来存储子部件,在Component接口中实现与部分有关操作. 以公司构成

  • 设计模式系列之组合模式及其在JDK和MyBatis源码中的运用详解

    组合模式及其在JDK源码中的运用 前言组合和聚合什么是组合模式示例透明组合模式透明组合模式的缺陷安全组合模式 组合模式角色组合模式在JDK源码中的体现组合模式应用场景享元模式优缺点总结 前言 本文主要会讲述组合模式的用法,并会结合在JDK和MyBatis源码中的运用来进一步理解组合模式. 在编码原则中,有一条是:多用组合,少用继承.当然这里的组合和我们今天要讲的组合模式并不等价,这里的组合其实就是一种聚合,那么聚合和组合有什么区别呢? 组合和聚合 人在一起叫团伙,心在一起叫团队.用这句话来诠释组

  • Android设计模式系列之组合模式

    Android中对组合模式的应用,可谓是泛滥成粥,随处可见,那就是View和ViewGroup类的使用.在android UI设计,几乎所有的widget和布局类都依靠这两个类. 组合模式,Composite Pattern,是一个非常巧妙的模式.几乎所有的面向对象系统都应用到了组合模式. 1.意图 将对象View和ViewGroup组合成树形结构以表示"部分-整体"的层次结构(View可以做为ViewGroup的一部分). 组合模式使得用户对单个对象View和组合对象ViewGrou

  • C# 设计模式系列教程-组合模式

    1. 概述 将对象组合成树形结构以表示"部分-整体"的层次结构.组合模式使得用户对单个对象和组合对象的使用具有一致性. 2. 解决的问题 当希望忽略单个对象和组合对象的区别,统一使用组合结构中的所有对象(将这种"统一"性封装起来). 3. 组合模式中的角色 3.1 组合部件(Component):它是一个抽象角色,为要组合的对象提供统一的接口. 3.2 叶子(Leaf):在组合中表示子节点对象,叶子节点不能有子节点. 3.3 合成部件(Composite):定义有枝

  • 通过JDK源码分析关闭钩子详解

    关闭钩子 用户关闭关闭程序,需要做一些善后的清理工作,但问题是,某些用户不会按照推荐的方法关闭应用程序,肯能导致善后工作无法进行.像tomcat调用server的start方法启动容器,然后会逐级调用start.当发出关闭命令是会启动关闭功能,但是关闭可能会有一些意外产生,导致应用程序没有进入到我们制定的关闭方法去.如何解决这个问题呢,使得即使有意外也能正常进入关闭流程. 好在java提供了一种优雅的方式去解决这种问题.使得关闭的善后处理的代码能执行.java的关闭钩子能确保总是执行,无论用户如

  • 浅谈Java设计模式系列-装饰器模式

    一.概述 装饰器模式作用是针对目标方法进行增强,提供新的功能或者额外的功能. 不同于适配器模式和桥接模式,装饰器模式涉及的是单方,和代理模式相同,而且目标必须是抽象的. 而实际上,装饰器模式和代理模式的实现方式基本一致,只在目标的存在上有些差别,这个后面我们具体讲述. 二.初步分析 上面提到了两点: 涉及的是单方 目标是抽象的 我们来想一下,所谓单方主要指的是在整个装饰器模式中不存在双方调用,要解决的也不是双方调用的问题,而是解决单方提供对外服务的问题,这个单方在自行对外提供服务时,功能不足,或

  • Python设计模式结构型组合模式

    目录 一.组合模式 二.应用场景 三.代码示例 一.组合模式 组合,将多个对象组合成为一个树状结构,来表示业务逻辑上的层次.组合模式使得用户对单个对象和组合对象的使用具有一致性. 比如,描述一家公司的层次结构,那么我们用办公室来表示节点,则总经理办公司是根节点,下面分别由人事办公室.业务办公室.生产办公室.财务办公室,每个办公室下面可以还有跟小的办公室,每个办公室都有职责.人员数.人员薪资等属性: 优点: 定义了包含基本对象和组合对象的类层次结构. 简化 Client 代码,即 Client 可

  • JDK源码中一些实用的“小技巧”总结

    前言 这段时间比较闲,就看起了jdk源码.一般的一个高级开发工程师, 能阅读一些源码对自己的提升还是蛮大的.本文总结了一些JDK源码中的"小技巧",分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧. 1 i++ vs i-- String源码的第985行,equals方法中 while (n--!= 0) { if (v1[i] != v2[i]) return false; i++; } 这段代码是用于判断字符串是否相等,但有个奇怪地方是用了i--!=0来做判断,我们通

  • SpringBoot学习系列之MyBatis Plus整合封装的实例详解

    前言 MyBatis-Plus是一款MyBatis的增强工具(简称MP),为简化开发.提高效率,但我们并没有直接使用MP的CRUD接口,而是在原来的基础上封装一层通用代码,单表继承我们的通用代码,实现了单表的基础get.save(插入/更新).list.page.delete接口,使用Vo去接收.传输数据,实体负责与数据库表映射. 这样做的目的是与我们之前的那套jpa保持编码风格上的一致,当我们的通用接口不能满足要求时,应当先考虑使用MP的Service层CRUD接口,然后是Mapper的接口,

  • 最通俗的白话讲解JDK源码中的ThreadLocal

    目录 引言 ThreadLocal是什么?它能干什么? ThreadLocal实现线程隔离的秘密 为什么ThreadLocal会出现OOM的问题? 内存泄漏演示 内存泄漏问题分析 父子线程的参数传递 总结 引言 其实网上有很多关于ThreadLocal的文章了,有不少文章也已经写的非常好了.但是很多同学反应还有一些部分没有讲解的十分清楚,还是有一定的疑惑没有想的十分清楚.因此本文主要结合常见的一些疑问.ThreadLocal源码.应用实例以注意事项来全面而深入地再详细讲解一遍ThreadLoca

  • Java设计模式之java组合模式详解

    目录 引言 组合模式介绍 角色 模式结构 示例代码 水果盘 文件浏览 更复杂的组合模式 透明与安全 透明组合模式 安全组合模式 组合模式总结 优点 缺点 适用场景 应用 XML文档解析 文件 HashMap Mybatis SqlNode中的组合模式 参考文章 总结 引言 树形结构不论在生活中或者是开发中都是一种非常常见的结构,一个容器对象(如文件夹)下可以存放多种不同的叶子对象或者容器对象,容器对象与叶子对象之间属性差别可能非常大. 由于容器对象和叶子对象在功能上的区别,在使用这些对象的代码中

随机推荐