努力保头发的打工人
努力保头发的打工人
发布于 2021-01-24 / 30 阅读 / 0 评论 / 0 点赞

组合模式

1 概述

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

通用类图如下:

组合模式定义.png

  • component 定义参与组合对象共有的方法和属性,可以定义一些默认行为或属性。

  • leaf 叶子对象,下面没有分支,遍历中最小单位。

  • composite 树枝对象,作用是组合树枝节点和叶子节点形成一个树的结构。

2 代码案例

英雄联盟中每件装备的合成图,有是树的结构,不过会有相同的小件(基础装备),我们把它看成是不同对象就可以使用组合模式来描述。

首先创建所有装备的抽象类,定义一些公共的方法

/**
 * @author chiangtaol
 * @date 2020-12-07
 * @describe 装备抽象类
 */
@Getter
@Setter
public abstract class EquipmentAbstract {
​
    /**
     * 名称
     */
    private String name;
​
    /**
     * 属性值
     */
    private int attribute;
​
    /**
     * 父节点
     */
    private EquipmentAbstract parent;
​
    /**
     * 构造方法
     */
    public EquipmentAbstract(String name, int attribute) {
        this.name = name;
        this.attribute = attribute;
    }
​
    /**
     * 获取销售价格
     */
    public abstract double getSellingPrice();
​
    /**
     * 打印装备详情
     */
    public String getInfo() {
        StringBuffer sb = new StringBuffer();
        sb.append("名称:").append(this.getName()).append(",")
                .append("属性:").append(this.getAttribute()).append(",")
                .append("购买价格:").append(this.getSellingPrice());
        return sb.toString();
    }
}

创建合成装备,和基础装备

/**
 * @author chiangtaol
 * @date 2020-12-07
 * @describe 合成装备, 非基础装备
 */
public class CompoundEquipment extends EquipmentAbstract{
​
    /** 合成价格*/
    private double compoundPrice;
​
    /** 由那些装备合成*/
    private ArrayList<EquipmentAbstract> list = new ArrayList<>();
​
    public ArrayList<EquipmentAbstract> getList() {
        return list;
    }
​
    public void addList(EquipmentAbstract equipment) {
        equipment.setParent(this);
        this.list.add(equipment);
    }
​
    public double getCompoundPrice() {
        return compoundPrice;
    }
​
    public void setCompoundPrice(double compoundPrice) {
        this.compoundPrice = compoundPrice;
    }
​
    /**
     * 构造方法
     *
     * @param name
     * @param attribute
     */
    public CompoundEquipment(String name, int attribute) {
        super(name, attribute);
    }
​
    /**
     * 获取销售价格
     */
    @Override
    public double getSellingPrice() {
        double price = compoundPrice;
        for (EquipmentAbstract equipment : list){
            price = price + equipment.getSellingPrice();
        }
        return price;
    }
      
    /**
     * 打印装备详情
     */
    @Override
    public String getInfo() {
        return super.getInfo()+",其中合成价格"+compoundPrice;
    }
}
​
​
/**
 * @author chiangtaol
 * @date 2020-12-07
 * @describe 基础装备
 */
public class BaseEquipment extends EquipmentAbstract {
​
    /**
     * 商品售价
     */
    private double sellingPrice;
​
    /**
     * 构造方法
     *
     * @param name      名称
     * @param attribute 属性值
     */
    public BaseEquipment(String name, int attribute) {
        super(name, attribute);
    }
​
    /**
     * 获取销售价格
     */
    @Override
    public double getSellingPrice() {
        return this.sellingPrice;
    }
​
    /**
     * 设置销售价格
     */
    public void setSellingPrice(double sellingPrice) {
        this.sellingPrice = sellingPrice;
    }
}

测试代码:

@Test
public void test(){
    CompoundEquipment equipment = build();
    System.out.println(equipment.getInfo());
    System.out.println(getInfo(equipment));
}
​
public String getInfo(CompoundEquipment equipment) {
    String info = "";
​
    ArrayList<EquipmentAbstract> list = equipment.getList();
    for (EquipmentAbstract ea : list){
        if (ea instanceof BaseEquipment){
            info = info + ea.getInfo() +"\n";
        }else {
            info = info + ea.getInfo() +"\n" + getInfo((CompoundEquipment) ea);
        }
    }
return info;
}
​
public CompoundEquipment build(){
    //小件
    BaseEquipment lsj = new BaseEquipment("蓝水晶",5);
    lsj.setSellingPrice(350d);
    CompoundEquipment yg = new CompoundEquipment("耀光",99);
    yg.setCompoundPrice(350);
    yg.addList(lsj);
​
​
    BaseEquipment cj = new BaseEquipment("长剑",5);
    cj.setSellingPrice(350d);
    BaseEquipment hsj = new BaseEquipment("红水晶",5);
    hsj.setSellingPrice(350d);
    CompoundEquipment js = new CompoundEquipment("净蚀",95);
    js.setCompoundPrice(350);
    js.addList(cj);
    js.addList(hsj);
​
    CompoundEquipment fc = new CompoundEquipment("蜂刺",94);
    fc.setCompoundPrice(350);
    BaseEquipment dj1 = new BaseEquipment("短剑",5);
    dj1.setSellingPrice(300d);
    BaseEquipment dj2 = new BaseEquipment("短剑",5);
    dj2.setSellingPrice(300d);
    fc.addList(dj1);
    fc.addList(dj2);
​
    //大件
    CompoundEquipment sanxiangzhili = new CompoundEquipment("三项之力",999);
    sanxiangzhili.setCompoundPrice(300d);
    sanxiangzhili.addList(js);
    sanxiangzhili.addList(fc);
    sanxiangzhili.addList(yg);
    return sanxiangzhili;
}
运行结果如下:
名称:三项之力,属性:999,购买价格:3000.0,其中合成价格300.0
名称:净蚀,属性:95,购买价格:1050.0,其中合成价格350.0
名称:长剑,属性:5,购买价格:350.0
名称:红水晶,属性:5,购买价格:350.0
名称:蜂刺,属性:94,购买价格:950.0,其中合成价格350.0
名称:短剑,属性:5,购买价格:300.0
名称:短剑,属性:5,购买价格:300.0
名称:耀光,属性:99,购买价格:700.0,其中合成价格350.0
名称:蓝水晶,属性:5,购买价格:350.0

3 优缺点

3.1 优点
  • 高层模块调用简单

  • 节点自由增加

3.2 缺点

使用场景类,直接使用了实现类,与依赖倒置原则冲突

4 使用场景

一般用于整体-部分关系的场景,如:菜单,文件、文件夹管理