13-Java代码审计-设计模式-组合模式

154 阅读5分钟

Java设计模式-组合模式(Composite Pattern)

目录

  • 什么是组合模式
  • 组合模式的2种类型
  • JavaSE组合模式的应用
  • Struts2组合模式的应用

“将对象组合成树形结构以表示“部分-整体”的层次结构,使得用户对单个对象和组合对象的使用具有一致性。”

一、什么是组合模式

组合模式又成为整体-部分模式,它是将对象组合成树状结构的层次结构的模式,大家想一下大学学习的二叉树就能理解了,有树枝节点与叶子节点,例如下面的图,是不是很容易理解了

image-20230317153528008

image-20230317155443970

树枝和叶子的区别是是否有子节点,树枝中应该知道自己有哪些子节点,这种形式涉及到三个角色:

  • **抽象构件(Component)角色:**这是一个抽象角色,它给参加组合的对象定义出公共的接口及其默认行为,可以用来管理所有的子对象。组合对象通常把它所包含的子对象当做类型为Component的对象。在安全式的组合模式里,构建角色并不定义出管理子对象的方法,这一定义由树枝构件对象给出。
  • **树叶构件(Leaf)角色:**树叶对象没有下级子对象的对象,定义出参加组合的原始对象的行为。
  • **树枝构件(Composite)角色:**代表参加组合的有下级子对象的对象。树枝构件类给出所有的管理子对象的方法,如add()remove()、以及getChild()等。

二、组合模式的2种类型

上面提到树枝和叶子的区别是是否有子节点,而如何管理子节点将是他们的区别,是让叶子和树枝都有管理子节点的能力,还是只让树枝有管理子节点的能力,这种理念的区别造成组合模式有2种:

  1. 安全式组合模式:安全模式的组合模式要求管理子节点的方法只出现在树枝构件类中,而不出现在树叶构件类中
  2. 透明式组合模式:透明式的组合模式要求所有的具体构建类,不论树枝构件还是树叶构件,均符合一个固定接口

image-20230317160125562

image-20230317160143526

第一张图是安全模式,第二张是透明模式;透明模式的Leaf类的printStruct()是空方法或者接近空方法的简单实现。

为什么叫安全模式呢?是从客户端角度看是否安全,因为叶子对象和容器对象在本质上是有区别的,叶子对象不可能有下一个层次的对象,即不可能包含成员对象,因此为其提供 add()、remove() 等方法是没有意义的,这在编译阶段不会出错,但在运行阶段如果调用这些方法可能会出错(如果没有提供相应的错误处理代码)

透明模式是Component生命力所有的方法,哪怕Leaf不会用到getChild,同样也需要实现,对所有子类方法都是很透明的,所以叫透明模式。

两种模式各有优点,安全模式不会出现误操作,减少未知问题;透明模式对客户端透明,无需关心类型,以统一的方式操作,更加符合组合模式的定义:【用户对单个对象和组合对象的使用具有一致性】

用哪种模式都可以,我们以安全模式举例,先看下UML图

image-20230317171818233

代码如下

package org.CompositePattern.version1;

import java.util.ArrayList;
import java.util.List;

/**
 * 树枝与树叶的抽象类
 */
abstract class Component{
    private String name;
    private String position;
    private int salary;

    public Component(String name, String position, int salary){
        this.name = name;
        this.position = position;
        this.salary = salary;
    }

    public String getInfo(){
        String res = "姓名:" + this.name + "," + "职位:" + this.position + "," + "薪水:" + this.salary;
        System.out.println(res);
        return res;
    }
}

class Leaf extends Component{

    public Leaf(String name, String position, int salary) {
        super(name, position, salary);
    }
}

/**
 * 树枝类,也就是节点类,内部有添加树叶,获取树叶信息的方法
 */
class Branch extends Component{
    List<Component> staffList = new ArrayList<>();

    public Branch(String name, String position, int salary) {
        super(name, position, salary);
    }

    public void addStaff(Component component){
        this.staffList.add(component);
    }

    public List<Component> getStaffs(){

        for (Component com:this.staffList ) {
            com.getInfo();
            // 如果是树枝,则获取树枝下的叶子
            if (com instanceof Branch){
                ((Branch) com).getStaffs();
            }
        }
        return this.staffList;
    }
}
public class Test {
    public static void main(String[] args) {
        Branch ceo = init();
        // CEO要知道自己所有员工的信息
        ceo.getStaffs();
    }
    // 准备数据,一般来自数据库
    public static Branch init(){
        Leaf zhangsan = new Leaf("张三","员工",6000);
        Leaf lisi = new Leaf("李四","员工",5000);
        Leaf wangwu = new Leaf("王五","员工",4000);
        Branch dev = new Branch("张明","研发部领导",20000);
        dev.addStaff(zhangsan);
        dev.addStaff(lisi);
        dev.addStaff(wangwu);

        Leaf liuning = new Leaf("刘宁","员工",6000);
        Branch sell = new Branch("王伟","销售部领导",20000);
        sell.addStaff(liuning);

        Branch ceo = new Branch("陈真","CEO",60000);
        ceo.addStaff(dev);
        ceo.addStaff(sell);
        return ceo;
    }
}
// 运行结果
姓名:张明,职位:研发部领导,薪水:20000
姓名:张三,职位:员工,薪水:6000
姓名:李四,职位:员工,薪水:5000
姓名:王五,职位:员工,薪水:4000
姓名:王伟,职位:销售部领导,薪水:20000
姓名:刘宁,职位:员工,薪水:6000

三、JavaSE组合模式的应用

HashMap使用了组合模式,分析下类的关系

  • **抽象构件(Component)角色:**Map
  • **树叶构件(Leaf)角色:**Node
  • **树枝构件(Composite)角色:**HashMap

HashMap是一个树枝构件,定义了数据的存储方式,内部定义了静态内部类的数组Node<K,V>[] tab,用于存储数据,HashMap有如下方法

  1. put,加入一个叶子节点
  2. remove,删除一个叶子节点
  3. get,获取一个节点信息

看个案例

package org.CompositePattern.version2;

import java.util.HashMap;

class People{
    private String name;
    private int age;
    public People(String name, int age){
        this.name = name;
        this.age = age;
    }
}

public class TestHashMap {
    public static void main(String[] args) {
        HashMap ceo = new HashMap();
        // 开发部
        HashMap dev = new HashMap();
        dev.put("zhangsan",new People("zhangsan",20));
        dev.put("lisi",new People("lisi",20));
        // 销售部
        HashMap sell = new HashMap();
        sell.put("zhangsan",new People("zhangsan",20));
        sell.put("lisi",new People("lisi",20));
        //老板管2个部门
        ceo.put("dev",dev);
        ceo.put("sell",sell);

    }
}

HashMap维护了和前面案例类似的组织架构

四、Struts2组合模式的应用

暂时没发现,如果读者有发现,还请私信我添加,谢谢