zoukankan      html  css  js  c++  java
  • 设计模式--组合模式

    组合模式

    简介

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

    组合关系与聚合关系的区别:

    1、组合关系:也表示类之间整体和部分的关系,但是组合关系中部分和整体具有统一的生存期。一旦整体对象不存在,部分对象也将不存在。部分对象与整体对象之间具有共生死的关系。(具有相同的生命周期)。

    2、聚合关系:指的是整体与部分的关系。通常在定义一个整体类后,再去分析这个整体类的组成结构。从而找出一些组成类,该整体类和组成类之间就形成了聚合关系。例如一个航母编队包括海空母舰、驱护舰艇、舰载飞机及核动力攻击潜艇等(具有不同的生命周期)。

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

    由上图可以看出,其实根节点和树枝节点本质上是同一种数据类型。可以作为容器使用;而叶子节点与树枝节点在语义上不属于同一种类型,但是在组合模式中,会把树枝节点和叶子节点认为是同一种数据类型(用同一接口定义),让它们具备一致行为。这样,在组合模式中,整个树形结构中的对象u是同一种类型,带来的一个好处就是客户无需辨别树枝节点还是叶子节点,而是可以直接操作,给客户使用带来极大的便利。

    组合模式的UML图:

    image-20210510224716995

    组合模式包含3个角色:

    1、抽象根节点(Component):定义系统各层次对象的共有方法和属性,可以预先定义一些默认行为和属性;

    2、树枝节点(Composite):定义树枝节点的行为,存储子节点,组合树枝节点和叶子节点形成一个树形结构;

    3、叶子节点(Leaf):叶子节点对象,其下再无分支,是系统层次便利的最小单位。

    组合模式在代码具体实现上,有两种不同的方式,分别是透明组合模式和安全组合模式。

    组合模式的应用场景

    当子系统与其内各个对象层次呈现树形结构时,可以使用组合模式让子系统内各个对象层次的行为操作具备一致性。客户端使用该子系统内任意一个层次对象时,无需进行区分,直接使用通用操作即可,为客户端的使用带来了便捷。

    1、希望客户端可以忽略组合对象与单个对象的差异时;

    2、对象层次具备整体和部分,呈树形结构。

    生活中的树形菜单,操作系统目录结构,公司组织架构等。

    透明组合模式

    透明组合模式是把所有公共方法都定义在Component中,这样做的好处是客户端无需分辨是叶子节点(Leaf)还是树枝节点(Composite),它们具备完全一致的接口。上面的UML图就是透明模式。

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

    安全组合模式

    安全组合模式是只规定系统各个层次的最基础的一致行为,而把组合(树节点)本身的方法(管理子类对象的添加,删除等)放到自身当中。其中UML类图如下:

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

    组合模式在源码中的应用

    1、HashMap中的putAll()方法

    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节点就是叶子节点。中间构件就会有规定的存储方式。HashMap中的存储方式就是一个静态内部类的数组Node<K,V> tab

    2、常用的ArrayList对象的addAll()方法,其参数也是ArrayList的父类Collection

    public boolean addAll(Collection<? extends E> c) {
        Object[] a = c.toArray();
        int numNew = a.length;
        ensureCapacityInternal(size + numNew);  // Increments modCount
        System.arraycopy(a, 0, elementData, size, numNew);
        size += numNew;
        return numNew != 0;
    }
    

    3、Mybatis解析各种Mapping文件的SQL,设计的类叫做SqlNode,xml中的每一个Node都会解析为SqlNode对象,最后把所有的SqlNode拼装到一起就形成一条完整的SQL语句。

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

    apply()方法会根据传入的参数context,参数解析该SqlNode所记录的SQL片段,并调用DynamicContenxt.appendSql()方法将解析后的SQL片段追加到DynamicContenxt的sqlBuilder中保存。当SQL节点下的所有SqlNode完成解析后,可以通过DynamicContenxt.getSql()获取一条完成的SQL语句。

    组合模式的优缺点

    ​ 既然组合模式分为两种实现,那么肯定是不同的场合某一种会更加合适,也即具体情况具体分析。透明组合模式将公共接口封装到抽象根节点(Component)中,那么系统所有节点就具备一致性行为,所以如果当系统绝大多数层次具备相同的公共行为时,采用透明组合模式会更好一些。(代价:为剩下少数层次节点引入不需要的方法);而如果当前系统各个层次差异性行为较多或者树节点层次相对稳定(健壮)时,采用安全组合模式。

    设计模式的理解应该是重于意而不是形,真正编码时,经常使用的是某种设计模式的变形体。真正切合项目的模式才是正确的设计模式。

    优点:

    1、清楚的定义分层次的复杂对象,表示对象的全部或部分层次

    2、让客户端忽略了层次的差异,方便对整个层次结构进行控制

    3、简化客户端代码

    4、符合开闭原则

    缺点:

    1、限制类型时会较为复杂

    2、使设计变得更加抽象

    代码链接

    组合模式

  • 相关阅读:
    那么 Appium 到底是怎么工作的呢?
    最小的 Velocity 教程
    CentOS 7 使用经验(更新中)
    [TL-WR841N V5~V9] 如何当作无线交换机使用?
    VS2012打包部署Winform程序
    ASP.NET 应用程序生命周期概述[转自MSDN]
    (翻译)从底层了解ASP.NET体系结构 [转]
    .NET在IE9中页面间URL传递中文变成乱码的解决办法
    终于把你必须知道的.NET看完了
    装箱与拆箱的操作与内存管理
  • 原文地址:https://www.cnblogs.com/snail-gao/p/15068432.html
Copyright © 2011-2022 走看看