组合模式 Composite Pattern

一、模式介绍

1.1、定义

也叫整体-部分(Part-Whole)模式,将一组对象组织成树形结构,以表示一种整体-部分的层次结构。组合让客户端可以统一单个对象和组合对象的处理逻辑。客户端代指代码的使用者。

组合模式一般用来描述整体与部分的关系,它将对象组织到树形结构中,顶层节点被称为根节点,根节点下面可以包含树枝节点和叶子节点,树枝节点下面又可以包含树枝节点和叶子节点。
在这里插入图片描述
从图中可以看出,根节点和树枝节点本质上属于同一种数据类型,可以作为容器使用;而叶子节点与树枝节点在语义上不属于同一种类型。但是在组合模式中,会把树枝节点和叶子节点看作同一种数据类型(用统一接口定义),让它们具备一致行为。

这样,在组合模式中,整个树形结构中的对象都属于同一种类型,带来的好处就是用户不需要辨别是树枝节点还是叶子节点,可以直接进行操作,给用户带来极大的便利。

1.2、优点

  1. 组合模式使得客户端代码可以一致的处理单个对象和组合对象,无须关心自己处理的是单个对象,还是组合对象,这简化了客户端代码
  2. 更容易在组合体内加入新的对象,客户端不会因为加入了新的对象而更改源代码,满足开闭原则

1.3、缺点

  1. 设计较复杂,客户端需要花更多时间理清类之间的层次关系
  2. 不容易限制容器中的构件
  3. 不容易用继承的方法来增加构件的新功能

二、结构与实现

2.1、结构

  1. 抽象构件角色(Component):为树叶构件和树枝构件声明公共接口,并实现它们默认的行为。总的抽象类或接口,定义一些通用的方法,比如新增、删除、更新等
  2. 树叶构件角色(Leaf):是组合中的叶节点对象,它没有子节点,用于继承或实现抽象构件
  3. 树枝构件角色(Composite):是组合中的分支节点对象,他有子节点,用于继承和实现抽象构件。它的主要作用是存储和管理子部件,通常包含 add()、remove()、getChild() 等方法

组合模式分为:透明式的组合模式和安全式的组合模式

  • 在透明式的组合模式中,抽象构建还声明访问和管理子类的接口,管理工作由树枝构件完成
  • 在安全式的组合模式中,抽象构件角色不声明访问和管理子类的接口,管理工作由树枝构件完成。

透明式的组合模式
在这里插入图片描述
安全式组合模式
在这里插入图片描述

2.2、实现

2.2.1、类图

在这里插入图片描述

2.2.2、MenuComponent
package com.erlang.composite;

/**
 * @description: 菜单组件
 * @author: erlang
 * @since: 2022-02-15 21:45
 */
public abstract class MenuComponent {

    public void add(MenuComponent menuComponent) {
        throw new UnsupportedOperationException();
    }

    public void remove(MenuComponent menuComponent) {
        throw new UnsupportedOperationException();
    }

    public MenuComponent getChild(int index) {
        throw new UnsupportedOperationException();
    }

    public String getName() {
        throw new UnsupportedOperationException();
    }

    public String getDescription() {
        throw new UnsupportedOperationException();
    }

    public double getPrice() {
        throw new UnsupportedOperationException();
    }

    public boolean isVegetarian() {
        throw new UnsupportedOperationException();
    }

    public void print() {
        throw new UnsupportedOperationException();
    }

}
2.2.3、MenuItem
package com.erlang.composite;

/**
 * @description: 菜单元素
 * @author: erlang
 * @since: 2022-02-15 21:45
 */
public class MenuItem extends MenuComponent {

    String name;
    String description;
    boolean vegetarian;
    double price;

    public MenuItem(String name, String description, boolean vegetarian, double price) {
        this.name = name;
        this.description = description;
        this.vegetarian = vegetarian;
        this.price = price;
    }

    @Override
    public String getName() {
       return name;
    }

    @Override
    public String getDescription() {
        return description;
    }

    @Override
    public double getPrice() {
        return price;
    }

    @Override
    public boolean isVegetarian() {
        return vegetarian;
    }

    @Override
    public void print() {
        System.out.print(" " + getName());
        // 是否为素食
        if (isVegetarian()) {
            System.out.print("(素食)");
        }
        System.out.println(", " + getPrice());
        System.out.println("   -- " + getDescription());
    }
}
2.2.4、Menu
package com.erlang.composite;

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

/**
 * @description: 菜单
 * @author: erlang
 * @since: 2022-02-15 21:46
 */
public class Menu extends MenuComponent {

    private List<MenuComponent> children = new ArrayList<>();
    private String name;
    private String description;

    public Menu(String name, String description) {
        this.name = name;
        this.description = description;
    }

    @Override
    public void add(MenuComponent menuComponent) {
        children.add(menuComponent);
    }

    @Override
    public void remove(MenuComponent menuComponent) {
        children.remove(menuComponent);
    }

    @Override
    public MenuComponent getChild(int index) {
        return children.get(index);
    }

    @Override
    public String getName() {
        return name;
    }

    @Override
    public String getDescription() {
        return description;
    }

    @Override
    public void print() {
        System.out.print(" " + getName());
        System.out.println(", " + getDescription());
        System.out.println("-----------------------------");

        Iterator<MenuComponent> iterator = children.iterator();
        while (iterator.hasNext()) {
            iterator.next().print();
        }
    }
}
2.2.5、Waitress
package com.erlang.composite;

/**
 * @description: 服务员
 * @author: erlang
 * @since: 2022-02-15 21:45
 */
public class Waitress {

    MenuComponent menus;

    public Waitress(MenuComponent menus) {
        this.menus = menus;
    }

    public void printMenu() {
        menus.print();
    }
}
2.2.6、MenuTestDrive
package com.erlang.composite;

/**
 * @description:
 * @author: erlang
 * @since: 2022-02-15 22:04
 */
public class MenuTestDrive {

    public static void main(String[] args) {
        MenuComponent pancakeHouseMenu = new Menu("PANCAKE HOUSE", "Breakfast");
        MenuComponent dinnerMenu = new Menu("DINNER MENU", "Lunch");
        MenuComponent cafeMenu = new Menu("CAFE MENU", "Dinner");
        MenuComponent dessertMenu = new Menu("DESSERT MENU", "Dessert of course");

        MenuComponent menus = new Menu("ALl MENUS", "All menus combined");
        menus.add(pancakeHouseMenu);
        menus.add(dinnerMenu);
        menus.add(cafeMenu);

        dinnerMenu.add(new MenuItem("意大利面", "这是意大利面", true, 1.1));
        dinnerMenu.add(dessertMenu);

        dessertMenu.add(new MenuItem("披萨", "这是披萨", true, 1.2));

        Waitress waitress = new Waitress(menus);
        waitress.printMenu();
    }

}
2.2.7、执行结果
 ALl MENUS, All menus combined
-----------------------------
 PANCAKE HOUSE, Breakfast
-----------------------------
 DINNER MENU, Lunch
-----------------------------
 意大利面(素食), 1.1
   -- 这是意大利面
 DESSERT MENU, Dessert of course
-----------------------------
 披萨(素食), 1.2
   -- 这是披萨
 CAFE MENU, Dinner
-----------------------------
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值