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

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

这篇文章主要介绍了组合模式及其在JDK和MyBatis源码中的运用,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

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

前言

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

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

组合和聚合

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

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

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

什么是组合模式

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

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

图片.png

讲了这么多,感觉有点抽象,所以依然是老规矩: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,这就是一种组合模式的体现:

图片.png

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

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

图片.png

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

图片.png

组合模式应用场景

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

享元模式优缺点

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

总结

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

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

相关文章
设计模式 存储 人工智能
219 0
|
6月前
|
设计模式 存储 缓存
Netty源码—9.性能优化和设计模式
本文主要介绍了Netty的两大性能优化工具、FastThreadLocal的源码和总结、Recycler的设计理念/使用/四个核心组件/初始化/对象获取/对象回收/异线程收割对象和总结,以及Netty设计模式的单例模式和策略模式。
212 53
|
7月前
|
设计模式 Java 定位技术
【设计模式】【结构型模式】组合模式(Composite)
一、入门 什么是组合模式 组合模式(Composite Pattern)是一种结构型设计模式,它允许你将对象组合成树形结构来表示“部分-整体”的层次关系。组合模式使得客户端可以统一处理单个对象和组合对
229 10
|
10月前
|
设计模式 存储 Java
「全网最细 + 实战源码案例」设计模式——责任链模式
责任链模式(Chain of Responsibility Pattern)是一种行为型设计模式,允许将请求沿着处理者链进行发送。每个处理者可以处理请求或将其传递给下一个处理者,从而实现解耦和灵活性。其结构包括抽象处理者(Handler)、具体处理者(ConcreteHandler)和客户端(Client)。适用于不同方式处理不同种类请求、按顺序执行多个处理者、以及运行时改变处理者及其顺序的场景。典型应用包括日志处理、Java Web过滤器、权限认证等。
215 13
「全网最细 + 实战源码案例」设计模式——责任链模式
|
10月前
|
设计模式 存储 算法
「全网最细 + 实战源码案例」设计模式——命令模式
命令模式(Command Pattern)是一种行为型设计模式,将请求封装成独立对象,从而解耦请求方与接收方。其核心结构包括:Command(命令接口)、ConcreteCommand(具体命令)、Receiver(接收者)和Invoker(调用者)。通过这种方式,命令的执行、撤销、排队等操作更易扩展和灵活。 适用场景: 1. 参数化对象以操作。 2. 操作放入队列或远程执行。 3. 实现回滚功能。 4. 解耦调用者与接收者。 优点: - 遵循单一职责和开闭原则。 - 支持命令组合和延迟执行。 - 可实现撤销、恢复功能。 缺点: - 增加复杂性和类数量。
329 14
「全网最细 + 实战源码案例」设计模式——命令模式
|
10月前
|
设计模式 算法 开发者
「全网最细 + 实战源码案例」设计模式——策略模式
策略模式(Strategy Pattern)是一种行为型设计模式,用于定义一系列可替换的算法或行为,并将它们封装成独立的类。通过上下文持有策略对象,在运行时动态切换算法,提高代码的可维护性和扩展性。适用于需要动态切换算法、避免条件语句、经常扩展算法或保持算法独立性的场景。优点包括符合开闭原则、运行时切换算法、解耦上下文与策略实现、减少条件判断;缺点是增加类数量和策略切换成本。示例中通过定义抽象策略接口和具体策略类,结合上下文类实现动态算法选择。
330 8
「全网最细 + 实战源码案例」设计模式——策略模式
|
10月前
|
设计模式 SQL 算法
「全网最细 + 实战源码案例」设计模式——模板方法模式
模板方法模式是一种行为型设计模式,定义了算法的骨架并在父类中实现不变部分,将可变部分延迟到子类实现。通过这种方式,它避免了代码重复,提高了复用性和扩展性。具体步骤由抽象类定义,子类实现特定逻辑。适用于框架设计、工作流和相似算法结构的场景。优点包括代码复用和符合开闭原则,缺点是可能违反里氏替换原则且灵活性较低。
245 7
「全网最细 + 实战源码案例」设计模式——模板方法模式
|
10月前
|
设计模式 存储 安全
「全网最细 + 实战源码案例」设计模式——组合模式
组合模式(Composite Pattern)是一种结构型设计模式,用于将对象组合成树形结构以表示“部分-整体”的层次结构。它允许客户端以一致的方式对待单个对象和对象集合,简化了复杂结构的处理。组合模式包含三个主要组件:抽象组件(Component)、叶子节点(Leaf)和组合节点(Composite)。通过这种模式,客户端可以统一处理简单元素和复杂元素,而无需关心其内部结构。适用于需要实现树状对象结构或希望以相同方式处理简单和复杂元素的场景。优点包括支持树形结构、透明性和遵循开闭原则;缺点是可能引入不必要的复杂性和过度抽象。
320 22
|
10月前
|
设计模式 存储 缓存
「全网最细 + 实战源码案例」设计模式——享元模式
享元模式(Flyweight Pattern)是一种结构型设计模式,旨在减少大量相似对象的内存消耗。通过分离对象的内部状态(可共享、不变)和外部状态(依赖环境、变化),它有效减少了内存使用。适用于存在大量相似对象且需节省内存的场景。模式优点包括节省内存和提高性能,但会增加系统复杂性。实现时需将对象成员变量拆分为内在和外在状态,并通过工厂类管理享元对象。
371 92
|
11月前
|
设计模式 前端开发 数据库
「全网最细 + 实战源码案例」设计模式——桥接模式
桥接模式(Bridge Pattern)是一种结构型设计模式,通过将抽象部分与实现部分分离,使它们可以独立变化,从而降低代码耦合度,避免类爆炸,提高可扩展性。其结构包括实现类接口、具体实现类、抽象类和精确抽象类。适用于多维度扩展类、隐藏实现细节、简化庞杂类以及运行时切换实现方法的场景。优点包括高扩展性、隐藏实现细节、遵循开闭原则和单一职责原则;缺点是可能增加代码复杂度。示例中展示了不同操作系统播放不同格式视频文件的实现。
247 19

热门文章

最新文章