津津乐道设计模式 - 组合模式详解(以餐厅菜单系统举例让你快速掌握)

在这里插入图片描述

😄 19年之后由于某些原因断更了三年,23年重新扬帆起航,推出更多优质博文,希望大家多多支持~
🌷 古之立大事者,不惟有超世之才,亦必有坚忍不拔之志
🎐 个人CSND主页——Micro麦可乐的博客
🐥《Docker实操教程》专栏以最新的Centos版本为基础进行Docker实操教程,入门到实战
🌺《RabbitMQ》本专栏主要介绍使用JAVA开发RabbitMQ的系列教程,从基础知识到项目实战
🌸《设计模式》专栏以实际的生活场景为案例进行讲解,让大家对设计模式有一个更清晰的理解
如果文章能够给大家带来一定的帮助!欢迎关注、评论互动~

什么是组合模式

组合模式是一种结构型设计模式,它允许将对象组合成树形结构以表示“整体-部分”的层次关系。组合模式使得用户对单个对象和组合对象的使用具有一致性,可以用统一的方式处理它们

组合模式由以下几个角色组成:

组件(Component):定义了组合对象和叶子对象的共有方法和属性,并且可以为子对象提供默认的实现。它可以是抽象类或接口。

叶子(Leaf):表示组合中的叶子对象,没有子对象。

组合(Composite):表示组合中的组合对象,可以包含子对象。它实现了组件的方法,并在需要的情况下调用子对象的方法。

客户端(Client):通过组合模式的接口来操作组合对象和叶子对象。

在这里插入图片描述

组合模式适用场景

  • 当需要表示对象的部分-整体层次结构,并希望以统一的方式处理整体和部分时,可以使用组合模式。例如,文件系统中的目录和文件的层次结构。
  • 当希望客户端忽略组合对象和叶子对象的区别,统一地对待它们时,可以使用组合模式。客户端不需要知道当前操作的是组合对象还是叶子对象,只需要调用统一的接口即可。
  • 当希望对组合对象和叶子对象进行增加、删除、遍历等操作时,可以使用组合模式。组合模式使得对整个树形结构的操作更加简单和灵活。

需要注意的是,组合模式并不是为了解决对象的层次关系而存在的,而是通过将对象组合成树形结构来统一处理整体和部分的关系。在应用组合模式时,需要合理划分组合对象和叶子对象,并确保它们之间的接口和行为一致性,以达到更好的扩展性和灵活性。

生活场景

在餐厅中,菜单通常是由多个菜品组成的。我们可以将菜单系统抽象为一个树形结构,其中树的节点表示菜单或菜品分类,而叶子节点表示具体的菜品。

在这里插入图片描述

餐厅的菜单系统中可能包含以下节点和叶子节点:

菜单节点(Menu):表示一个完整的菜单,可能包含多个菜品或菜单分类

菜单分类节点(Menu Category):表示菜单中的分类,如主菜、甜点、饮品等。这个节点可以包含多个叶子节点和子菜单节点

菜品节点(Dish):表示具体的菜品,包含菜品的名称、描述和价格等信息

通过组合模式,我们可以使用统一的接口来操作菜单系统。无论是获取整个菜单,还是获取某个分类下的菜品,我们都可以使用相同的方式来处理整体和部分。

例如,我们可以调用菜单节点的方法来获取整个菜单的内容,并在其中递归调用子菜单节点的方法来获取菜单分类下的具体菜品。同样地,我们也可以调用菜单分类节点的方法来获取该分类下的菜品列表。

组合模式的好处在于,我们可以方便地对菜单系统进行扩展和修改,无需改变客户端的代码。例如,如果菜单中新增了一个新的菜品分类,我们只需要添加一个新的菜单分类节点即可,而不会影响到其他部分。

总而言之,组合模式可以让我们以统一的方式处理菜单系统中的整体和部分,提供了更好的扩展性和灵活性。

代码案例

首先,我们定义一个抽象的组件接口 MenuComponent,表示菜单系统中的组件(菜单、菜单分类和菜品):

public interface MenuComponent {
    void display();
}

然后,我们创建具体的菜单节点类 Menu,表示一个完整的菜单:

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

public class Menu implements MenuComponent {
    private String name;
    private List<MenuComponent> components;

    public Menu(String name) {
        this.name = name;
        this.components = new ArrayList<>();
    }

    public void addComponent(MenuComponent component) {
        components.add(component);
    }

    public void removeComponent(MenuComponent component) {
        components.remove(component);
    }

    @Override
    public void display() {
        System.out.println("菜单:" + name);
        System.out.println("--------------------");
        for (MenuComponent component : components) {
            component.display();
            System.out.println();
        }
    }
}

接下来,我们创建菜单分类节点类 MenuCategory,表示菜单中的分类:

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

public class MenuCategory implements MenuComponent {
    private String name;
    private List<MenuComponent> components;

    public MenuCategory(String name) {
        this.name = name;
        this.components = new ArrayList<>();
    }

    public void addComponent(MenuComponent component) {
        components.add(component);
    }

    public void removeComponent(MenuComponent component) {
        components.remove(component);
    }

    @Override
    public void display() {
        System.out.println("菜单分类:" + name);
        System.out.println("*******************");
        for (MenuComponent component : components) {
            component.display();
        }
    }
}

最后,我们创建菜品节点类 Dish,表示具体的菜品:

public class Dish implements MenuComponent {
    private String name;
    private String description;
    private double price;

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

    @Override
    public void display() {
        System.out.println("菜品:" + name);
        System.out.println("描述:" + description);
        System.out.println("价格:" + price + "元");
    }
}

现在,我们可以在客户端代码中使用组合模式来操作菜单系统:

public class CombinationPatternTest {
    public static void main(String[] args) {
        // 创建菜单
        Menu menu = new Menu("餐厅菜单");

        // 创建菜单分类
        MenuCategory mainCourse = new MenuCategory("主菜");
        MenuCategory dessert = new MenuCategory("甜点");

        // 创建具体菜品
        Dish steak = new Dish("牛排", "美味的牛排", 50.0);
        Dish pasta = new Dish("意大利面", "经典的意式面食", 30.0);
        Dish cake = new Dish("蛋糕", "甜甜的蛋糕", 20.0);

        // 将菜品添加到相应的菜单分类中
        mainCourse.addComponent(steak);
        mainCourse.addComponent(pasta);
        dessert.addComponent(cake);

        // 将菜单分类添加到菜单中
        menu.addComponent(mainCourse);
        menu.addComponent(dessert);

        // 显示整个菜单
        menu.display();
    }
}

通过以上代码,我们创建了一个餐厅的菜单系统,并使用组合模式来组织菜单、菜单分类和菜品之间的关系。客户端代码可以通过调用菜单的 display() 方法来展示整个菜单的内容。

组合模式的优缺点

组合模式的优点:

  • 简化客户端代码:组合模式通过统一的接口,使得客户端可以一致地处理单个对象和组合对象,无需区分它们的具体类型,从而简化了客户端代码的复杂性。
  • 增加新组件方便:由于组合模式使用了递归结构,因此添加新的组件非常方便。无论是添加一个新的叶子节点还是一个新的组合节点,都可以通过修改少量代码来完成。
  • 灵活性和扩展性:组合模式使得系统具备了较高的灵活性和可扩展性。可以根据需要构建复杂的组合结构,随时增加、删除或替换组件,而无需修改现有代码。

组合模式的缺点:

  • 设计复杂性增加:由于组合模式涉及到递归和层次结构的组织,因此设计和实现时会增加一定的复杂性,特别是在处理具有多层嵌套的组合结构时。
  • 不适合所有情况:组合模式更适合表示树形结构的场景,如果对象之间的关系不是层次化的,或者对象的行为不一致,使用组合模式可能不太合适。
  • 运行效率低:在使用组合模式时,由于需要通过递归遍历整个组合结构,可能会导致性能下降。特别是当组合结构很大时,遍历的时间会显著增加。

综上所述,组合模式通过统一接口、灵活的组合结构和简化客户端代码的特点,为处理树形结构的对象提供了一种有效的方式。然而,使用组合模式需要权衡设计复杂性和运行效率,确保在合适的场景下使用。

结语

本章节主要介绍了组合模式、组合模式适用场景、组合模式的优缺点,并以餐厅菜单的生活场景模拟组合模式的样例代码,如果本文对你有用,欢迎关注收藏评论,后续将陆续推出贴切生活的搞笑讲解方式带大家一起学编程~

样例代码:https://github.com/lhmyy521125/toher-designmode

  • 15
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 8
    评论
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Micro麦可乐

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值