组合模式(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 来遍历不同菜单,这更适合扁平化的数据结构。

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

相关推荐
暮湫11 分钟前
泛型(2)
java
南宫生20 分钟前
力扣-图论-17【算法学习day.67】
java·学习·算法·leetcode·图论
转码的小石28 分钟前
12/21java基础
java
李小白6636 分钟前
Spring MVC(上)
java·spring·mvc
GoodStudyAndDayDayUp1 小时前
IDEA能够从mapper跳转到xml的插件
xml·java·intellij-idea
装不满的克莱因瓶1 小时前
【Redis经典面试题六】Redis的持久化机制是怎样的?
java·数据库·redis·持久化·aof·rdb
n北斗1 小时前
常用类晨考day15
java
骇客野人2 小时前
【JAVA】JAVA接口公共返回体ResponseData封装
java·开发语言
yuanbenshidiaos3 小时前
c++---------数据类型
java·jvm·c++
向宇it3 小时前
【从零开始入门unity游戏开发之——C#篇25】C#面向对象动态多态——virtual、override 和 base 关键字、抽象类和抽象方法
java·开发语言·unity·c#·游戏引擎