组合模式(Composite Pattern)

使用组合模式(Composite Pattern)是一个更优雅的方式来表示菜单和菜单项。组合模式允许我们将单个对象(如菜单项)和组合对象(如菜单)以相同的方式处理。

解决方案:

  1. 创建组合结构 :我们将菜单项和菜单抽象为 MenuComponent,其中菜单项是叶节点,菜单是组合节点。
  2. Menu:可以包含子菜单或菜单项。
  3. MenuItem:代表具体的菜单项。
  4. Waitress :只需处理一个 MenuComponent 对象,并能够遍历所有的菜单和菜单项。

代码实现

java 复制代码
import java.util.Iterator;

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

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

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

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

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

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

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

    public Iterator<MenuComponent> createIterator() {
        throw new UnsupportedOperationException();
    }
}
java 复制代码
public class MenuItem extends MenuComponent {
    private String name;
    private String description;
    private double price;

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

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

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

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

    @Override
    public void print() {
        System.out.println("  " + getName() + ": " + getDescription() + " -- ¥" + getPrice());
    }
}
java 复制代码
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class Menu extends MenuComponent {
    private List<MenuComponent> menuComponents = 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) {
        menuComponents.add(menuComponent);
    }

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

    @Override
    public MenuComponent getChild(int i) {
        return menuComponents.get(i);
    }

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

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

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

        for (MenuComponent menuComponent : menuComponents) {
            menuComponent.print();
        }
    }
}
4. 实现 Waitress
java 复制代码
public class Waitress {
    private MenuComponent allMenus;

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

    public void printMenu() {
        allMenus.print();
    }
}
5. 修改测试代码
java 复制代码
public class CompositePatternDemo {
    public static void main(String[] args) {
        MenuComponent breakfast = new Menu("早餐菜单", "早餐6:00~9:00");
        MenuComponent lunch = new Menu("午餐菜单", "午餐11:30~14:30");
        MenuComponent coffeeMenu = new Menu("咖啡菜单", "全天24小时供应");

        MenuComponent allMenus = new Menu("所有菜单", "所有可使用的菜单");

        // Add breakfast items
        breakfast.add(new MenuItem("胡辣汤", "素胡辣汤", 3));
        breakfast.add(new MenuItem("油条", "按斤称", 2));
        breakfast.add(new MenuItem("包子", " 莲藕馅", 1.5));
        // Add lunch items
        lunch.add(new MenuItem("茄汁面", "番茄鸡蛋汤面", 8));
        lunch.add(new MenuItem("蒜汁面", "凉拌面", 7));
        lunch.add(new MenuItem("臊子面", "羊肉臊子面", 15));

        // Add coffee items
        coffeeMenu.add(new MenuItem("生椰拿铁", "少冰", 9.99));
        coffeeMenu.add(new MenuItem("丝绒拿铁", "热饮", 12.99));
        coffeeMenu.add(new MenuItem("茉莉生椰拿铁", "外卖,少冰,不另外加糖", 16.99));

        // Combine menus
        allMenus.add(breakfast);
        allMenus.add(lunch);
        allMenus.add(coffeeMenu);

        // Create waitress and print all menus
        Waitress waitress = new Waitress(allMenus);
        waitress.printMenu();
    }
}

输出结果

plaintext 复制代码
所有菜单: 所有可使用的菜单
---------------------

早餐菜单: 早餐6:00~9:00
---------------------
  胡辣汤: 素胡辣汤 -- ¥3.0
  油条: 按斤称 -- ¥2.0
  包子:  莲藕馅 -- ¥1.5

午餐菜单: 午餐11:30~14:30
---------------------
  茄汁面: 番茄鸡蛋汤面 -- ¥8.0
  蒜汁面: 凉拌面 -- ¥7.0
  臊子面: 羊肉臊子面 -- ¥15.0

咖啡菜单: 全天24小时供应
---------------------
  生椰拿铁: 少冰 -- ¥9.99
  丝绒拿铁: 热饮 -- ¥12.99
  茉莉生椰拿铁: 外卖,少冰,不另外加糖 -- ¥16.99

代码解释

  1. MenuComponent 抽象类 :定义了菜单和菜单项的通用操作,如 add()remove()getChild() 等。组合对象(Menu)可以包含 MenuComponent,而叶节点(MenuItem)则只处理具体的菜单项。
  2. Menu:可以包含子菜单或菜单项,通过组合来管理多个菜单和子菜单。
  3. MenuItem:叶节点,表示具体的菜单项。
  4. Waitress :只需要处理一个 MenuComponent 对象,无论是叶节点(菜单项)还是组合对象(菜单),都可以通过相同的方式处理并打印。

总结

通过使用组合模式,MenuMenuItem 都被视为 MenuComponentWaitress 只需要一个 MenuComponent 对象即可遍历所有菜单和菜单项。这使得代码非常灵活,易于扩展和维护。

组合模式 (Composite Pattern) 详细解释

1. 组合模式的概念

组合模式允许我们将对象组合成树形结构来表示"部分-整体"的层次结构。它使得客户端能够以一致的方式处理单个对象和组合对象。

在我们的场景中,菜单项(MenuItem)是基本的元素,菜单(Menu)则是组合对象,可以包含其他菜单或菜单项。Waitress 类不需要知道它是在处理菜单还是菜单项,只要调用通用接口即可,这正是组合模式的强大之处。

2. 类结构概述
  • MenuComponent :抽象基类,提供了所有菜单和菜单项的通用操作。它定义了组合和叶节点的接口,如 add()remove()getChild()print() 等操作。

    由于菜单项不需要支持 add()getChild(),而菜单可以包含其他菜单或菜单项,因此这些操作在基类中默认抛出 UnsupportedOperationException,具体的子类可以根据需要覆盖这些方法。

  • Menu :组合对象类,代表一个菜单,可以包含多个 MenuComponent(既可以是子菜单,也可以是菜单项)。它的主要功能是管理子菜单和菜单项,提供添加、删除、获取子组件的操作。

  • MenuItem :叶节点类,代表一个具体的菜单项。它没有子节点,所以不能包含其他 MenuComponent,只能提供自己的基本信息,如名称、描述和价格。

  • Waitress :客户端类,接收一个 MenuComponent,可以遍历和打印所有菜单和菜单项。它对组合和叶节点一视同仁,只调用 print() 方法即可打印出菜单结构。

3. 组合模式的工作原理

通过 MenuComponent 抽象类,Waitress 可以直接使用 MenuComponent 的统一接口,来处理菜单和菜单项的组合结构。组合模式的核心在于它让我们能够像处理单个对象一样,处理整个对象的组合。无论是遍历单个菜单项还是遍历包含多个子菜单的菜单,Waitress 类只需要关心调用通用的 print() 方法。

4. 组合模式的优点
  • 一致性:客户端可以一致地处理叶节点(菜单项)和组合节点(菜单),使得客户端代码变得简洁且灵活。

    在我们实现的 Waitress 类中,它只关心如何遍历和打印 MenuComponent,不需要区分是在处理一个具体的菜单项还是一个包含子菜单的组合对象。

  • 可扩展性 :添加新的菜单或菜单项变得非常简单。我们可以在组合中嵌套更多的子菜单,也可以轻松添加新的菜单项,甚至在 Menu 中组合多个 Menu

  • 简化客户端代码 :由于客户端只需要处理抽象基类 MenuComponent,不需要分别处理 MenuItemMenu,客户端代码变得非常简洁。这种设计也让系统变得更灵活和可维护。

5. 组合模式中的递归

组合模式的核心在于它是一个递归结构:菜单可以包含子菜单,子菜单又可以包含更多的菜单或菜单项。这种递归结构允许我们通过遍历树形结构来处理所有的菜单项。Menuprint() 方法就是递归调用,遍历所有子菜单和菜单项。

6. 与迭代器模式的对比

虽然组合模式和迭代器模式都能处理多个对象,但它们的目标和使用场景有所不同:

  • 迭代器模式 :用于顺序遍历一个集合中的元素,通常用在结构较为扁平的情况下,例如遍历数组或列表中的元素。之前的实现中,我们通过多个 Iterator 来遍历不同菜单,这更适合扁平化的数据结构。

  • 组合模式:适合处理树形结构(如菜单-子菜单-菜单项),在这种情况下,组合模式能够让客户端以统一的方式处理复杂的层次结构。

相关推荐
零千叶1 小时前
【面试】AI大模型应用原理面试题
java·设计模式·面试
坐吃山猪5 小时前
SpringBoot01-配置文件
java·开发语言
我叫汪枫6 小时前
《Java餐厅的待客之道:BIO, NIO, AIO三种服务模式的进化》
java·开发语言·nio
yaoxtao6 小时前
java.nio.file.InvalidPathException异常
java·linux·ubuntu
Swift社区7 小时前
从 JDK 1.8 切换到 JDK 21 时遇到 NoProviderFoundException 该如何解决?
java·开发语言
DKPT8 小时前
JVM中如何调优新生代和老生代?
java·jvm·笔记·学习·spring
phltxy8 小时前
JVM——Java虚拟机学习
java·jvm·学习
seabirdssss10 小时前
使用Spring Boot DevTools快速重启功能
java·spring boot·后端
喂完待续10 小时前
【序列晋升】29 Spring Cloud Task 微服务架构下的轻量级任务调度框架
java·spring·spring cloud·云原生·架构·big data·序列晋升