【Java设计模式】结构型设计模式-组合模式(十二)

组合模式

  • 组合模式的也叫做整体-部分模式,它是一种将对象组合成树状的层次结构的模式,用来表示"整体-部分"的关系,使用户对单个对象和组合对象具有一致的访问性。

进一步阐述:

  • 组合模式一般用来描述整体与部分的关系,它将对象组织到树形结构中,顶层的节点被称为根节点,根节点下面可以包含树枝节点和叶子节点,树枝节点下面又可以包含树枝节点和叶子节点。其实就是我们常说的"树型结构"。

组合模式包含四种角色:

  • Component(抽象构件角色):它的主要作用是为树叶构件和树枝构件声明公共接口,并实现它们的默认行为。在透明式的组合模式中抽象构件还声明访问和管理子类的接口;在安全式的组合模式中不声明访问和管理子类的接口,管理工作由树枝构件完成。(总的抽象类或接口,定义一些通用的方法,比如新增、删除)
  • Leaf(树叶构件角色):是组合中的叶节点对象,它没有子节点,用于继承或实现抽象构件。
  • Composite(树枝构件角色 / 中间构件角色):是组合中的分支节点对象,它有子节点,用于继承和实现抽象构件。它的主要作用是存储和管理子部件,通常包含 Add()、Remove()、GetChild() 等方法。

案例:实现当用户在商店购物后,显示其所选商品信息,并计算所选商品总价的功能。

UML类图:

客户端Client类:

java 复制代码
/**
 * 使用组合模式的客户端
 */
public class Client {
​
    public static void main(String[] args) {
        Bags bigBag = new Bags("大商品袋");
        Bags mediumBag = new Bags("中商品袋");
        Bags smallBag = new Bags("小商品袋");
​
        Goods sneakers = new Goods("运动鞋", 1, 1198);
        Goods snacks = new Goods("零食", 5, 5);
        Goods vegetables = new Goods("蔬菜", 5, 1.5);
​
        //中商品袋中放入运动鞋
        mediumBag.add(sneakers);
        //小商品袋中放入零食和蔬菜
        smallBag.add(snacks);
        smallBag.add(vegetables);
        //把中商品袋和小商品袋一起放入大商品袋组合起来
        bigBag.add(mediumBag);
        bigBag.add(smallBag);
        //打印所有的商品信息
        bigBag.show();
        double total = bigBag.calculation();
        System.out.println("要支付的总价是:" + total);
    }
}

商店物品Articles接口:

java 复制代码
/**
 * 商店物品(抽象构件)
 */
public interface Articles {
​
    /**
     * 计算方法
     */
    double calculation();
​
    /**
     * 展示信息
     */
    void show();
}

袋子Bags类:

java 复制代码
/**
 * 袋子类(树枝构件)
 */
public class Bags implements Articles {
​
    /**
     * 袋子名称
     */
    private String name;
​
    /**
     * 持有商品的集合
     */
    private List<Articles> bags = new ArrayList<>();
​
    public Bags(String name) {
        this.name = name;
    }
​
    /**
     * 添加商品
     */
    public void add(Articles articles) {
        bags.add(articles);
    }
​
    /**
     * 删除商品
     */
    public void remove(Articles articles) {
        bags.remove(articles);
    }
​
    /**
     * 获取某一个商品
     */
    public Articles getChild(int i) {
        return bags.get(i);
    }
​
    /**
     * 计算所有价格
     */
    public double calculation() {
        BigDecimal total = new BigDecimal("0");
        for (Articles bag : bags) {
            total = total.add(BigDecimal.valueOf(bag.calculation()));
        }
        return total.doubleValue();
    }
​
    /**
     * 打印所有商品信息
     */
    public void show() {
        for (Articles bag : bags) {
            bag.show();
        }
    }
}

商品Goods类:

java 复制代码
/**
 * 商品类(树叶构件)
 */
public class Goods implements Articles {
​
    /**
     * 商品名称
     */
    private String name;
​
    /**
     * 商品数量
     */
    private int quantity;
​
    /**
     * 商品单价
     */
    private double unitPrice;
​
    public Goods(String name, int quantity, double unitPrice) {
        this.name = name;
        this.quantity = quantity;
        this.unitPrice = unitPrice;
    }
​
    @Override
    public double calculation() {
        return BigDecimal.valueOf(unitPrice)
                .multiply(BigDecimal.valueOf(quantity)).doubleValue();
    }
​
    @Override
    public void show() {
        System.out.println(name + "(数量:" + quantity + ",单价:" + unitPrice + "元)");
    }
}

总结:

  • 组合模式使得客户端代码可以一致地处理单个对象和组合对象,无须关心自己处理的是单个对象,还是组合对象,这简化了客户端代码。
  • 更容易在组合体内加入新的对象,客户端不会因为加入了新的对象而更改源代码,满足"开闭原则"。
  • 设计较复杂,客户端需要花更多时间理清类之间的层次关系。不容易限制容器中的构件。不容易用继承的方法来增加构件的新功能。

典型运用场景举例:

  • 在需要表示一个对象整体与部分的层次结构的场合。例如:文件目录显示,多及目录呈现等。
  • 要求对用户隐藏组合对象与单个对象的不同,用户可以用统一的接口使用组合结构中的所有对象的场合,比如树形结构中的二叉树。
相关推荐
lizhongxuan1 小时前
AIOPS 的自治运维与可验证进化机制
后端
Warson_L4 小时前
python - set/tuple/dict quiz
后端
IT_Octopus5 小时前
Spring Boot 实战:@PostConstruct + Caffeine 缓存初始化与定时刷新
spring boot·后端·缓存
swipe5 小时前
从本地开发到生产部署:用 Docker Compose 跑通 NestJS、MySQL 与 Milvus
后端·langchain·llm
码事漫谈5 小时前
SenseNova Skills Studio:为商汤SenseNova U1打造的本地办公技能包
后端
zhangxingchao5 小时前
AI应用开发七:可以替代 RAG 的技术
前端·人工智能·后端
Java面试题总结6 小时前
java高频面试题(2026最新)
java·开发语言·jvm·数据库·spring·缓存
苦逼的猿宝6 小时前
学生心理咨询评估系统
java·毕业设计·springboot·计算机毕业设计
隔窗听雨眠6 小时前
doctype、charset、meta如何控制整个渲染流水线
java·服务器·前端
excel7 小时前
🧠 Prisma 表名大写 vs SQL 导出小写问题深度解析(附踩坑与解决方案)
前端·后端