乐趣区

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

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

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

一、引出问题

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

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

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

二、概念与应用

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

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

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

四、总结

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

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

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

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

创立型设计模式

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

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

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

结构型设计模式

四、设计模式之代理模式

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

六、桥接模式

退出移动版