Java设计模式之组合模式

本文通过老王和小王探讨书房、书架、各类书的管理问题,引出结构型设计模式家族中的一个重要成员——组合模式,本文会给予两种组合模式的典型代码实现,为了加深理解会在第三部分应用中介绍组合模式在源码中的实际运用,最后总结该设计模式学习后的一些思考。

读者可以拉取完整代码到本地进行学习,实现代码均测试通过后上传到码云,本地源码下载。

一、引出问题

上篇文章中老王给小王买车以后,小王对老王感激涕零,看着老王凌乱的书房,小王提出要帮助老王整理整理他的书架。

小王开始了他的分析。老王平时博览群书,中文、英文、梵文...每个语种占满了书架,而每个语种中又分经济学、计算机学、社会学等等类目。这是典型的分层次结构,将语种比作是图书的子类,类目是语种的子类结构划分。

将图书、语种、类目都看做是组织结构,他们之间没有继承关系,而是一个树形结构,可以更好的实现管理操作。

二、概念与使用

实际上,小王提出来的设计思路正是结构型设计模式中的组合模式,我们首先看一下组合模式的相关概念,组合模式(Composite Pattern),又叫部分整体模式,它创建了对象组的树形结构,将对象组合成树状结构以表示“整体-部分”的层次关系。组合模式依据树形结构来组合对象,用来表示部分以及整体层次。

组合模式使得用户对单个对象和组合对象的访问具有一致性,即:组合能让客户以一致的方式处理个别对象以及组合对象。

用大白话解释也就是,在实际应用中将所有图书依照树形模式进行组合,老王寻找书籍时,无论是访问某一类书还是某一个语种的书,使用同样的姿势即可,保证了访问的一致性。

在该模式中应该是有三个角色:

1、Root :这是组合中对象声明接口,在适当情况下,实现所有类共有的接口默认行为,用于访问和管理Root 子部件, Root 可以是抽象类或者接口。

2、Branches:非叶子节点用于存储子部件,在Root接口中实现了 子部件的相关操作。

2、Leaf : 在组合中表示叶子节点,叶子节点没有子节点。

小王分析的头头是道,老王提出来了他的疑问。

当我按语种查找还是按类目查找是使用的方法有时候是不一样的,如果你把所有方法都定义在Root中,在语种或者类目中实现中是无意义的,而且这违背了接口隔离原则。

小王觉得说的对也不对,如果我改成不在Root中定义,那么我在客户端调用的时候就需要判断是枝还是叶了,增加了繁杂的逻辑判断,而且相比另外一种变得不透明了,依赖倒置原则也没有遵守。

两种方式似乎都有缺陷,小王陷入了纠结不知道该如何取舍,老王提出了他的一些见解,没有任何一个设计模式是完全没有缺点的,两种都有各自的好处,在实际的运用中根据条件进行取舍,而正确选择的前提就是要对所有的设计模式充分的把握。

上面两种就对应组合模式中的两个大分类、①透明组合模式、安全组合模式。

①透明组合模式把所有的公共方法都定义在Root中,这样做的好处就是客户端无需分辨是叶子节点(Leaf)和树枝节点(Branches),他们具备完全一致的接口;缺点是叶子节点(Leaf)会继承得到一些它所不需要(管理子类操作的方法)的方法,这与设计模式接口隔离原则相违背。

②安全组合模式的好处是接口定义职责清晰,符合设计模式单一职责原则和接口隔离原则;缺点是客户需要区分树枝节点(Branches)和叶子节点(Leaf),这样才能正确处理各个层次的操作,客户端依赖抽象(Root),违背了依赖倒置原则。

我们把两种的方式实现,读者对比他们之间的区别。

安全模式

Root(根节点):

/**
 * @author tcy
 * @Date 08-08-2022
 */
public abstract class RootBook {
    protected String name;

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

    public abstract String operation();

}

Branches(树枝节点)

/**
 * @author tcy
 * @Date 08-08-2022
 */
public class BranchesLanguages extends RootBook {

    private List<RootBook> roots;

    public BranchesLanguages(String name) {
        super(name);
        this.roots = new ArrayList<RootBook>();
    }

    public String operation() {
        StringBuilder builder = new StringBuilder(this.name);
        for (RootBook component : this.roots) {
            builder.append("\n");
            builder.append(component.operation());
        }
        return builder.toString();

    }
    public boolean addChild(RootBook component) {
        return this.roots.add(component);
    }

    public boolean removeChild(RootBook component) {
        return this.roots.remove(component);
    }

    public RootBook getChild(int index) {
        return this.roots.get(index);
    }

}

Leaf(叶子节点)

/**
 * @author tcy
 * @Date 08-08-2022
 */
public class LeafClassify extends RootBook {

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

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

客户端:

/**
 * @author tcy
 * @Date 08-08-2022
 */
public class Client {

    public static void main(String[] args) {
        System.out.println("安全组合模式...");
        // 来一个根节点
        BranchesLanguages BranchesRoot = new BranchesLanguages("root/书");
        // 来一个树枝节点
        BranchesLanguages branchA = new BranchesLanguages("------branchA/英语");
        BranchesLanguages branchB = new BranchesLanguages("------branchB/中文");
        // 来一个叶子节点
        RootBook leafA = new LeafClassify("------leafA/经济学");
        RootBook leafB = new LeafClassify("------leafB/计算机学");
        RootBook leafC = new LeafClassify("------leafC/法学");

        BranchesRoot.addChild(branchA);
        BranchesRoot.addChild(leafC);
        branchA.addChild(leafA);
        branchA.addChild(branchB);
        branchB.addChild(leafB);

        String result = BranchesRoot.operation();
        System.out.println(result);

    }
}

透明模式

Root(根节点):

/**
 * @author tcy
 * @Date 08-08-2022
 */
public abstract class RootBook {
    protected String name;

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

    public abstract String operation();

    public boolean addChild(RootBook component) {
        throw new UnsupportedOperationException("addChild not supported!");
    }

    public boolean removeChild(RootBook component) {
        throw new UnsupportedOperationException("removeChild not supported!");
    }

    public RootBook getChild(int index) {
        throw new UnsupportedOperationException("getChild not supported!");
    }

}

Branches(树枝节点)

/**
 * @author tcy
 * @Date 08-08-2022
 */
public class BranchesLanguages extends RootBook {

    private List<RootBook> roots;

    public BranchesLanguages(String name) {
        super(name);
        this.roots = new ArrayList<RootBook>();
    }

    public String operation() {
        StringBuilder builder = new StringBuilder(this.name);
        for (RootBook component : this.roots) {
            builder.append("\n");
            builder.append(component.operation());
        }
        return builder.toString();

    }

    @Override
    public boolean addChild(RootBook component) {
        return this.roots.add(component);
    }

    @Override
    public boolean removeChild(RootBook component) {
        return this.roots.remove(component);
    }

    @Override
    public RootBook getChild(int index) {
        return this.roots.get(index);
    }

}

Leaf(叶子节点)

/**
 * @author tcy
 * @Date 08-08-2022
 */
public class LeafClassify extends RootBook {

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

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

客户端:

/**
 * @author tcy
 * @Date 08-08-2022
 */
public class Client {

    public static void main(String[] args) {

        System.out.println("透明组合模式...");
        // 来一个根节点
        RootBook BranchesRoot = new BranchesLanguages("root/书");
        // 来一个树枝节点
        RootBook branchA = new BranchesLanguages("------branchA/英语");
        RootBook branchB = new BranchesLanguages("------branchB/汉语");
        // 来一个叶子节点
        RootBook leafA = new LeafClassify("------leafA/计算机学");
        RootBook leafB = new LeafClassify("------leafB/法学");
        RootBook leafC = new LeafClassify("------leafC/社会学");

        BranchesRoot.addChild(branchA);
        BranchesRoot.addChild(leafC);
        branchA.addChild(leafA);
        branchA.addChild(branchB);
        branchB.addChild(leafB);

        String result = BranchesRoot.operation();
        System.out.println(result);

    }
}

使用组合模式的两种实现方法,这样就对老王的书架改造工程就完成了,对凭空捏造出来的需求有些读者看完想必还是云里雾里。我们结合JDK的源码和一些开发常用框架,再次深入源码对组合模式的使用。

三、应用

通过查询资料可知,组合模式在Jdk中的应用主要是集合类HashMap和Mybtis中的SqlNode。

我们分别看其实现。

1、jdk中HashMap的运用

在HashMap中有一个父类AbstractMap和一个子类Node。如下图

我们看下源代码:

 public class HashMap<K,V> extends AbstractMap<K,V>
    implements Map<K,V>, Cloneable, Serializable {
    ...
     public void putAll(Map<? extends K, ? extends V> m) {
        putMapEntries(m, true);
    }
    ...
    final void putMapEntries(Map<? extends K, ? extends V> m, boolean evict) {
        int s = m.size();
        if (s > 0) {
            if (table == null) { // pre-size
                float ft = ((float)s / loadFactor) + 1.0F;
                int t = ((ft < (float)MAXIMUM_CAPACITY) ?
                         (int)ft : MAXIMUM_CAPACITY);
                if (t > threshold)
                    threshold = tableSizeFor(t);
            }
            else if (s > threshold)
                resize();
            for (Map.Entry<? extends K, ? extends V> e : m.entrySet()) {
                K key = e.getKey();
                V value = e.getValue();
                putVal(hash(key), key, value, false, evict);
            }
        }
    }
    ...
}

putAll()方法传入的是Map对象,Map就是一个抽象构件(同时这个构件中只支持健值对的存储格式),而HashMap是一个中间构件,HashMap中的Node节点就是叶子节点。

Node是HashMap中的一个内部类,HashMap的存储节点指的正是Node,读者可以重点看这个类的实现。

在这个实例中,HashMap就是树枝节点,Node就是叶节点,Map就是根节点。

2、Mybtis中的SqlNode

SqlNode是一个接口,主要功能就是构造SQL语句。

public interface SqlNode {
  boolean apply(DynamicContext context);
}

SqlNode有一大堆的实现类,我们看其中的MixedSqlNode。

  public class MixedSqlNode implements SqlNode {
  private final List<SqlNode> contents;

  public MixedSqlNode(List<SqlNode> contents) {
    this.contents = contents;
  }

  @Override
  public boolean apply(DynamicContext context) {
    contents.forEach(node -> node.apply(context));
    return true;
  }
}

SqlNode就充当组合模式中的Root,而他的众多子类作用就在于拼接各种类型的SQL,在组合模式的角色中相当于树枝节点。其中在TrimSqlNode 中有一个子类WhereSqlNode就充当组合模式中的树叶节点。

这两个都属于组合模式中的典型例子,读者体会下使用这种模式的好处,和如果不使用组合模式应该怎样实现。

通过这两个例子我们应该可以看到,设计模式的使用中并不是完全遵循各自的角色,更多的是设计模式中的一些变种,读者不深入源码并不能了解到该模式的实现细节。读者需要做的就是尽可能的熟悉设计模式,在自己开发过程中可以“择优录取”。

四、总结

到这里组合模式也就介绍完了,这种模式的优缺点都非常的明显,优点就在于清楚的定义分层次的结构,在调用时忽略他们之间的差异,方便对整个层次进行控制,但是组合模式会违反依赖倒置原则。

理解是一回事,在实际应用中能正确的使用它就是另外一回事了。

读者要对每种设计模式都能做到心中有数,当我们在实际编程中,在潜意识里有各个设计模式的大体轮廓,参考代入进各种设计模式中,对于简化开发和易于维护性有没有好的帮助,选择一个最优的设计模式。

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对我们的支持。如果你想了解更多相关内容请查看下面相关链接

(0)

相关推荐

  • Java设计模式之组合模式的示例详解

    目录 定义 原理类图 案例 需求 方案 分析 总结 定义 组合模式,又叫部分整体模式,它创建了对象组的数据结构(将对象组合成树状结构,用来表示部分整体的层级关系)组合模式使得用户对单个对象和组合对象的访问具有一致性 原理类图 Component :这是组合模式中的抽象构件,他里面定义了所有类共有的默认行为,用来访问和管理Component的子部件,Component可以是抽象类,也可以是接口 leaf :在组合模式中表示叶子节点,叶子节点没有子节点了,他是最末端存放数据的结构 Composite

  • Java结构型设计模式之组合模式详解

    目录 组合模式 应用场景 优缺点 主要角色 组合模式结构 分类 透明组合模式 创建抽象根节点 创建树枝节点 创建叶子节点 客户端调用 安全组合模式 创建抽象根节点 创建树枝节点 创建叶子节点 客户端调用 组合模式 组合模式(Composite Pattern)也称为整体-部分(Part-Whole)模式,属于结构型模式. 它的宗旨是通过将单个对象(叶子节点)和组合对象(树枝节点)用相同的接口进行表示,使得客户端对单个对象和组合对象的使用具有一致性. 组合模式一般用来描述整体与部分的关系,它将对象

  • Java结构型设计模式之组合模式Composite Pattern详解

    目录 概述 三大组件 应用案例 顶层组件OrganizationComponent Composite组件 叶子节点 客户端测试 UML类图 总结 概述 组合模式(Composite Pattern),又叫部分整体模式,它创建了对象组的树形结构,将对象组合成树状结构以表示“整体-部分”的层次关系.组合模式依据树形结构来组合对象,用来表示部分以及整体层次. 这种类型的设计模式属于结构型模式. 组合模式使得用户对单个对象和组合对象的访问具有一致性,即:组合能让客户以一致的方式处理个别对象以及组合对象

  • Java设计模式之组合模式深入刨析

    目录 1.基本介绍 2.结构 3.组合模式解决的问题 4.组合模式解决学校院系展示 5.组合模式的注意事项和细节 1.基本介绍 1)组合模式(Composite Pattern),又叫部分整体模式,它创建了对象组的树形结构,将对象组合成树状结构以表示“整体-部分”的层次关系 2)组合模式依据树形结构来组合对象,用来表示部分以及整体层次 3)这种类型的设计模式属于结构型模式 4)组合模式使得用户对单个对象和组合对象的访问具有一致性,即:组合能让客户以一致的方式处理个别对象以及组合对象 2.结构 组

  • Java设计模式中的组合模式

    目录 模式介绍 UML类图 组合模式案例 组合模式的注意事项和细节 模式介绍 组合模式(Composite Pattern),又叫部分整体模式,它创建了对象组的树形结构,将对象组合成树状结构以表示“整体_部分”的层次关系. 组合模式依据树形结构来组合对象,用来表示部分以及整体层次. 这种类型的设计模式属于结构型模式. 组合模式使得用户对单个对象和组合对象的访问具有一致性,即:组合能让客户以一-致的方式处理个别对象以及组合对象 UML类图 类图解析: Component :这是组合中对象声明接口,

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

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

  • 分析Java设计模式之组合模式

    目录 一.概述 二. 模式定义 三. 模式结构 四. 模式实现 五. 模式优缺点 5.1.优点 5.2.缺点 六. 模式适用场景 七. 模式总结 一.概述 我们对于这个图片肯定会非常熟悉,这两幅图片我们都可以看做是一个文件结构,对于这样的结构我们称之为树形结构.在数据结构中我们了解到可以通过调用某个方法来遍历整个树,当我们找到某个叶子节点后,就可以对叶子节点进行相关的操作.我们可以将这颗树理解成一个大的容器,容器里面包含很多的成员对象,这些成员对象即可是容器对象也可以是叶子对象.但是由于容器对象

  • java设计模式之组合模式(Composite)

    概述 是一种结构型模式,将对象以树形结构组织起来,以表示"部分 - 整体"的层次结构,使得客户端对单个对象和组合对象的使用具有唯一性. UML类图 上面的类图包含的角色: Component:为参加组合的对象声明一个公共的接口,不管是组合还是叶节点. Leaf:在组合中表示叶子结点对象,叶子结点没有子结点. Composite:表示参加组合的有子对象的对象,并给出树枝构建的行为: 代码示例 import java.util.ArrayList; import java.util.Lis

  • Java设计模式之组合模式

    本文通过老王和小王探讨书房.书架.各类书的管理问题,引出结构型设计模式家族中的一个重要成员——组合模式,本文会给予两种组合模式的典型代码实现,为了加深理解会在第三部分应用中介绍组合模式在源码中的实际运用,最后总结该设计模式学习后的一些思考. 读者可以拉取完整代码到本地进行学习,实现代码均测试通过后上传到码云,本地源码下载. 一.引出问题 上篇文章中老王给小王买车以后,小王对老王感激涕零,看着老王凌乱的书房,小王提出要帮助老王整理整理他的书架. 小王开始了他的分析.老王平时博览群书,中文.英文.梵

  • 深入理解Java设计模式之组合模式

    目录 一.什么是组合模式 动机(Motivation) 意图(Intent) 二.组合模式的结构 结构图说明: 三.组合模式的使用场景 四.组合模式的优缺点 五.组合模式的实现 六.组合模式的.NET下应用 一.什么是组合模式 定义:将对象以树形结构组织起来,以达成"部分-整体"的层次结构,使得客户端对单个对象和组合对象的使用具有一致性. 动机(Motivation) 客户代码过多地依赖于对象容器复杂的内部实现结构,对象容器内部实现结构(而非抽象接口)的变化将引起客户代码的频繁变化,带

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

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

  • Java设计模式之桥接模式实例详解

    本文实例讲述了Java设计模式之桥接模式.分享给大家供大家参考,具体如下: 概念: 桥接模式(Bridge Pattern):将抽象部分与它的实现部分分离,使它们都可以独立地变化. 桥接模式将继承关系转换为关联关系,从而降低了类与类之间的耦合,减少了代码编写量. 什么情况下会用桥接模式? 简单的说就是我们在抽象对象的特征时,对象的特征属性又很抽象,不得不把属性再次抽象. 否则的话,具体子类的数量将会成几何增长,而且不易扩展.没办法维护现有代码. 举例,我们在抽象手机这二个对象时,它的几个属性,如

随机推荐