热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

关于设计模式:设计模式之组合模式

本文通过老王和小王探讨书房、书架、各类书的治理问题,引出结构型设计模式家族中的一个重要成员——组合模式,本文会给予两种组合模式的典型代码实现,为了加

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

读者能够拉取残缺代码到本地进行学习,实现代码均测试通过后上传到码云。

一、引出问题

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

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

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

二、概念与应用

实际上,小王提出来的设计思路正是结构型设计模式中的组合模式,咱们首先看一下组合模式的相干概念,组合模式(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 roots;

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


    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 roots;

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


    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 extends AbstractMap
    implements Map, Cloneable, Serializable {
    ...
     public void putAll(Map m) {
        putMapEntries(m, true);
    }
    ...
    final void putMapEntries(Map 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 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 contents;

  public MixedSqlNode(List contents) {
    this.cOntents= contents;
  }

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

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

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

通过这两个例子咱们应该能够看到,设计模式的应用中并不是齐全遵循各自的角色,更多的是设计模式中的一些变种,读者不深刻源码并不能理解到该模式的实现细节。读者须要做的就是尽可能的相熟设计模式,在本人开发过程中能够“择优录取”。

四、总结

到这里组合模式也就介绍完了,这种模式的优缺点都十分的显著,长处就在于分明的定义分档次的构造,在调用时疏忽他们之间的差别,不便对整个档次进行管制,然而组合模式会违反依赖倒置准则。

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

读者要对每种设计模式都能做到成竹在胸,当咱们在理论编程中,在潜意识里有各个设计模式的大体轮廓,参考代入进各种设计模式中,对于简化开发和易于维护性有没有好的帮忙,抉择一个最优的设计模式。

举荐读者,参考软件设计七大准则 认真浏览往期的文章,认真领会。

创立型设计模式

一、设计模式之工厂办法和形象工厂

二、设计模式之单例和原型

三、设计模式之建造者模式

结构型设计模式

四、设计模式之代理模式

五、设计模式之适配器模式

六、桥接模式


推荐阅读
  • Spring特性实现接口多类的动态调用详解
    本文详细介绍了如何使用Spring特性实现接口多类的动态调用。通过对Spring IoC容器的基础类BeanFactory和ApplicationContext的介绍,以及getBeansOfType方法的应用,解决了在实际工作中遇到的接口及多个实现类的问题。同时,文章还提到了SPI使用的不便之处,并介绍了借助ApplicationContext实现需求的方法。阅读本文,你将了解到Spring特性的实现原理和实际应用方式。 ... [详细]
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
  • 本文介绍了Oracle数据库中tnsnames.ora文件的作用和配置方法。tnsnames.ora文件在数据库启动过程中会被读取,用于解析LOCAL_LISTENER,并且与侦听无关。文章还提供了配置LOCAL_LISTENER和1522端口的示例,并展示了listener.ora文件的内容。 ... [详细]
  • Python正则表达式学习记录及常用方法
    本文记录了学习Python正则表达式的过程,介绍了re模块的常用方法re.search,并解释了rawstring的作用。正则表达式是一种方便检查字符串匹配模式的工具,通过本文的学习可以掌握Python中使用正则表达式的基本方法。 ... [详细]
  • 前景:当UI一个查询条件为多项选择,或录入多个条件的时候,比如查询所有名称里面包含以下动态条件,需要模糊查询里面每一项时比如是这样一个数组条件:newstring[]{兴业银行, ... [详细]
  • MyBatis多表查询与动态SQL使用
    本文介绍了MyBatis多表查询与动态SQL的使用方法,包括一对一查询和一对多查询。同时还介绍了动态SQL的使用,包括if标签、trim标签、where标签、set标签和foreach标签的用法。文章还提供了相关的配置信息和示例代码。 ... [详细]
  • 解决.net项目中未注册“microsoft.ACE.oledb.12.0”提供程序的方法
    在开发.net项目中,通过microsoft.ACE.oledb读取excel文件信息时,报错“未在本地计算机上注册“microsoft.ACE.oledb.12.0”提供程序”。本文提供了解决这个问题的方法,包括错误描述和代码示例。通过注册提供程序和修改连接字符串,可以成功读取excel文件信息。 ... [详细]
  • 图像因存在错误而无法显示 ... [详细]
  • 本文介绍了Python高级网络编程及TCP/IP协议簇的OSI七层模型。首先简单介绍了七层模型的各层及其封装解封装过程。然后讨论了程序开发中涉及到的网络通信内容,主要包括TCP协议、UDP协议和IPV4协议。最后还介绍了socket编程、聊天socket实现、远程执行命令、上传文件、socketserver及其源码分析等相关内容。 ... [详细]
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • 在Android开发中,使用Picasso库可以实现对网络图片的等比例缩放。本文介绍了使用Picasso库进行图片缩放的方法,并提供了具体的代码实现。通过获取图片的宽高,计算目标宽度和高度,并创建新图实现等比例缩放。 ... [详细]
  • Spring源码解密之默认标签的解析方式分析
    本文分析了Spring源码解密中默认标签的解析方式。通过对命名空间的判断,区分默认命名空间和自定义命名空间,并采用不同的解析方式。其中,bean标签的解析最为复杂和重要。 ... [详细]
  • 本文介绍了多因子选股模型在实际中的构建步骤,包括风险源分析、因子筛选和体系构建,并进行了模拟实证回测。在风险源分析中,从宏观、行业、公司和特殊因素四个角度分析了影响资产价格的因素。具体包括宏观经济运行和宏经济政策对证券市场的影响,以及行业类型、行业生命周期和行业政策对股票价格的影响。 ... [详细]
  • 关于我们EMQ是一家全球领先的开源物联网基础设施软件供应商,服务新产业周期的IoT&5G、边缘计算与云计算市场,交付全球领先的开源物联网消息服务器和流处理数据 ... [详细]
  • Tomcat/Jetty为何选择扩展线程池而不是使用JDK原生线程池?
    本文探讨了Tomcat和Jetty选择扩展线程池而不是使用JDK原生线程池的原因。通过比较IO密集型任务和CPU密集型任务的特点,解释了为何Tomcat和Jetty需要扩展线程池来提高并发度和任务处理速度。同时,介绍了JDK原生线程池的工作流程。 ... [详细]
author-avatar
金子祺_475
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有