案例:外卖系统案例

(一)、核心需求拆解

本次开发的核心目标是:在原有 “外卖菜品上架、浏览” 功能的基础上,新增 “通过菜品唯一 ID 精准查询菜品信息” 的功能,同时保证系统逻辑完整、用户交互友好。拆解为 3 个具体子需求:

  1. 数据层:为菜品实体新增 “唯一 ID” 属性,作为查询标识;
  2. 业务层:实现 “根据输入 ID 遍历菜品列表、匹配并返回结果” 的逻辑;
  3. 交互层:新增查询入口,完善用户输入、结果反馈(找到 / 未找到)的交互流程。

(二)、分阶段解题思路

阶段 1:数据模型改造(Food 类)
核心思路:扩展实体属性,保证数据唯一性
  1. 新增核心字段:在原有name/originalPrice等属性基础上,新增private int id字段,作为菜品的唯一标识(类似外卖平台的菜品编号);
  2. 完善构造方法
    • 保留无参构造,满足基础对象创建需求;
    • 扩展带参构造,加入id参数,支持创建时直接赋值;
  3. 补充访问方法:为id字段生成getId()/setId()方法,保证属性封装性(符合面向对象 “封装” 原则);
  4. 优化展示逻辑:重写toString()方法,将id融入菜品信息展示,方便用户查看 ID。
关键考量:
  • ID 必须唯一,因此不在用户输入环节手动填写,而是由系统自动分配(避免重复)。
阶段 2:业务逻辑扩展(FoodManager 类)
子步骤 1:设计 ID 自动生成机制
  1. 新增自增变量:在FoodManager中定义private int nextId = 1,初始值为 1,每次上架菜品后自增;
  2. 关联上架功能:修改addFood()方法,创建Food对象时,将nextId作为 ID 传入,随后nextId++,保证每个新菜品的 ID 唯一且连续(如第 1 个菜品 ID=1,第 2 个 = 2,以此类推)。
子步骤 2:实现 “ID 查询” 核心逻辑
  1. 入口设计:在主菜单中新增 “3. 根据 ID 查询菜品” 选项,关联queryFoodById()方法;
  2. 前置校验:查询前先判断菜品列表是否为空,若为空直接提示 “暂无菜品”,避免无效遍历;
  3. 用户输入处理:接收用户输入的目标 ID(int targetId),并处理输入后的换行符(避免后续输入异常);
  4. 遍历匹配逻辑
    • 定义Food targetFood = null作为 “匹配结果容器”;
    • 增强 for 循环遍历foodList,逐个对比菜品id与用户输入的targetId
    • 找到匹配项时,将菜品对象赋值给targetFood并终止循环(提升效率);
  5. 结果反馈
    • targetFood != null:打印匹配到的菜品详细信息;
    • targetFood == null:提示 “未找到对应 ID 的菜品”,保证用户体验。
子步骤 3:交互流程完善
  1. 菜单扩展:在showMenu()的 switch-case 中新增case 3,调用查询方法;
  2. 异常防护:对用户输入的非数字 ID(如字母、符号),虽基础版本未做校验,但预留了扩展空间(进阶可加输入合法性判断)。
阶段 3:程序入口保持稳定(FoodTest 类)
核心思路:开闭原则(对扩展开放、对修改关闭)
  1. 无需修改FoodTest类的任何代码,仅需创建FoodManager对象并调用showMenu()
  2. 原有 “上架、浏览” 功能不受影响,新增功能通过扩展实现,保证系统稳定性。

(三)、关键设计原则与考量

1. 面向对象核心原则
  • 封装性:菜品的id属性私有化,仅通过 Getter/Setter 访问,避免外部直接修改;
  • 开闭原则:新增查询功能时,仅扩展代码(新增方法 / 字段),不修改原有 “上架、浏览” 的核心逻辑;
  • 单一职责Food类仅负责封装菜品数据,FoodManager仅负责业务逻辑,FoodTest仅负责启动程序,职责清晰。
2. 业务逻辑合理性
  • ID 唯一性保障:系统自动分配 ID,而非用户输入,从源头避免 ID 重复;
  • 遍历效率:找到匹配 ID 后立即break终止循环,无需遍历全部列表,提升查询效率;
  • 用户体验
    • 查询前校验列表是否为空,避免无效操作;
    • 明确的结果反馈(找到 / 未找到),而非无提示或报错。
3. 扩展性预留
  • 查询方法独立封装为queryFoodById(),后续可基于此扩展 “ID 修改、ID 删除” 等功能;
  • 自增 ID 机制可灵活调整(如从 1000 开始编号,适配实际业务场景)。

(四)、核心思路总结

  1. 数据先行:先扩展实体属性(新增 ID),为查询功能提供数据基础;
  2. 逻辑闭环:查询功能包含 “输入→校验→遍历匹配→结果反馈” 完整流程,无逻辑断点;
  3. 兼容扩展:在不破坏原有功能的前提下扩展新功能,符合软件开发的核心原则,同时预留后续扩展空间(如 ID 删除、修改)。

二、完整代码示例

(一)、菜品类(Food.java)

封装菜品的核心信息,新增id字段用于唯一标识。

public class Food {
    // 菜品唯一ID
    private int id;
    // 菜品名称
    private String name;
    // 菜品原价
    private double originalPrice;
    // 菜品优惠价
    private double discountPrice;
    // 菜品其他信息(如口味、规格等)
    private String info;

    // 无参构造
    public Food() {}

    // 带参构造(包含id)
    public Food(int id, String name, double originalPrice, double discountPrice, String info) {
        this.id = id;
        this.name = name;
        this.originalPrice = originalPrice;
        this.discountPrice = discountPrice;
        this.info = info;
    }

    // Getter和Setter方法
    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public double getOriginalPrice() {
        return originalPrice;
    }

    public void setOriginalPrice(double originalPrice) {
        this.originalPrice = originalPrice;
    }

    public double getDiscountPrice() {
        return discountPrice;
    }

    public void setDiscountPrice(double discountPrice) {
        this.discountPrice = discountPrice;
    }

    public String getInfo() {
        return info;
    }

    public void setInfo(String info) {
        this.info = info;
    }

    // 重写toString方法,方便打印菜品信息
    @Override
    public String toString() {
        return "【ID:" + id + "】" + name + "\n" +
               "信息:" + info + "\n" +
               "原价:¥" + originalPrice + "  优惠价:¥" + discountPrice + "\n" +
               "------------------------";
    }
}

(二)、菜品管理类(FoodManager.java)

在原有功能基础上,新增根据 ID 查询菜品的功能。

import java.util.ArrayList;
import java.util.Scanner;

public class FoodManager {
    // 存储所有上架的菜品
    private ArrayList<Food> foodList = new ArrayList<>();
    // 扫描器,用于接收用户输入
    private Scanner sc = new Scanner(System.in);
    // 用于生成菜品ID(自增)
    private int nextId = 1;

    // 展示操作界面
    public void showMenu() {
        while (true) {
            System.out.println("\n===== 外卖商家系统 =====");
            System.out.println("1. 上架菜品");
            System.out.println("2. 浏览所有菜品");
            System.out.println("3. 根据ID查询菜品");
            System.out.println("4. 退出系统");
            System.out.print("请输入您的选择:");

            int choice = sc.nextInt();
            sc.nextLine(); // 处理换行符

            switch (choice) {
                case 1:
                    addFood();
                    break;
                case 2:
                    showAllFoods();
                    break;
                case 3:
                    queryFoodById();
                    break;
                case 4:
                    System.out.println("退出系统,感谢使用!");
                    return;
                default:
                    System.out.println("输入有误,请重新选择!");
            }
        }
    }

    // 上架菜品功能(自动分配ID)
    private void addFood() {
        System.out.println("\n===== 上架新菜品 =====");
        System.out.print("请输入菜品名称:");
        String name = sc.nextLine();

        System.out.print("请输入菜品原价:");
        double originalPrice = sc.nextDouble();

        System.out.print("请输入菜品优惠价:");
        double discountPrice = sc.nextDouble();
        sc.nextLine(); // 处理换行符

        System.out.print("请输入菜品其他信息(如口味、规格):");
        String info = sc.nextLine();

        // 创建菜品对象(自动生成ID)并加入集合
        Food food = new Food(nextId++, name, originalPrice, discountPrice, info);
        foodList.add(food);
        System.out.println("✅ 菜品【" + name + "】上架成功!菜品ID:" + food.getId());
    }

    // 浏览所有菜品功能
    private void showAllFoods() {
        System.out.println("\n===== 所有上架菜品 =====");
        if (foodList.isEmpty()) {
            System.out.println("⚠️ 暂无上架菜品,请先上架菜品!");
            return;
        }

        // 遍历集合,打印所有菜品信息
        for (Food food : foodList) {
            System.out.println(food);
        }
    }

    // 根据ID查询菜品功能(新增)
    private void queryFoodById() {
        System.out.println("\n===== 根据ID查询菜品 =====");
        if (foodList.isEmpty()) {
            System.out.println("⚠️ 暂无上架菜品,请先上架菜品!");
            return;
        }

        System.out.print("请输入要查询的菜品ID:");
        int targetId = sc.nextInt();
        sc.nextLine(); // 处理换行符

        // 遍历集合查找匹配ID的菜品
        Food targetFood = null;
        for (Food food : foodList) {
            if (food.getId() == targetId) {
                targetFood = food;
                break;
            }
        }

        // 输出查询结果
        if (targetFood != null) {
            System.out.println("✅ 查询到菜品信息:");
            System.out.println(targetFood);
        } else {
            System.out.println("❌ 未找到ID为【" + targetId + "】的菜品!");
        }
    }
}

(三)、测试类(FoodTest.java)

程序入口,启动商家管理系统。

public class FoodTest {
    public static void main(String[] args) {
        // 创建管理类对象并启动界面
        FoodManager manager = new FoodManager();
        manager.showMenu();
    }
}

三、核心知识点总结

(一)、面向对象编程核心

1. 封装特性强化
  • 属性封装:为菜品新增id私有字段,通过getId()/setId()方法控制访问,避免外部直接修改,保证数据安全性;
  • 方法封装:将 “ID 查询” 逻辑独立封装为queryFoodById()方法,与 “上架”“浏览” 方法分离,符合 “单一职责原则”,便于维护和复用。
2. 类的扩展与开闭原则
  • 扩展不修改:新增 ID 查询功能时,仅在原有Food类新增字段 / 方法、FoodManager类新增查询方法,未改动 “上架”“浏览” 核心逻辑,遵循 “对扩展开放、对修改关闭” 的开闭原则;
  • 构造方法设计:保留无参构造、扩展带参构造,兼顾不同场景下的对象创建需求(空对象初始化 / 全属性初始化)。

(二)、ArrayList 集合核心应用

1. 集合遍历与匹配
  • 增强 for 循环遍历:通过for (Food food : foodList)遍历菜品列表,逐个对比food.getId()与目标 ID,实现精准匹配;
  • 匹配结果处理:定义targetFood变量接收匹配结果,遍历中找到目标后立即break终止循环,提升查询效率。
2. 集合数据管理
  • 动态存储:利用ArrayList的动态扩容特性,存储数量不固定的菜品对象,无需担心长度限制;
  • 数据校验:查询前通过foodList.isEmpty()判断集合是否为空,避免空列表遍历导致的无效操作。

(三)、业务逻辑设计

1. 唯一标识(ID)设计
  • 自增 ID 机制:通过nextId自增变量为菜品自动分配唯一 ID,避免用户手动输入导致的重复问题,符合实际业务中 “菜品编号唯一” 的需求;
  • ID 关联展示:重写toString()方法,将 ID 融入菜品信息展示,为查询功能提供可视化标识。
2. 交互逻辑设计
  • 用户输入处理:使用Scanner接收用户输入的 ID,处理换行符避免输入异常;
  • 结果反馈:查询结果分为 “找到菜品(展示详情)” 和 “未找到菜品(提示信息)” 两种情况,保证交互的友好性和完整性。

(四)、Java 基础语法

1. 流程控制
  • 分支结构:在主菜单中通过switch-case新增查询功能入口,扩展程序功能;
  • 循环结构:增强 for 循环实现列表遍历,while(true)实现菜单循环展示。
2. 变量与数据类型
  • 整型变量应用idnextId使用int类型,适配菜品编号的数值特性;
  • 引用类型赋值:通过Food targetFood = null初始化引用变量,接收匹配到的菜品对象,利用 “引用指向对象” 的特性实现结果存储。

(五)、核心知识点速记

  1. 封装是面向对象的核心,私有属性需通过 Getter/Setter 访问,业务逻辑需封装为独立方法;
  2. ArrayList 遍历匹配是查询功能的核心,增强 for 循环简洁高效,匹配后及时终止循环可提升性能;
  3. 唯一 ID 设计需保证唯一性,系统自动分配(自增)是实际业务中最常用的实现方式;
  4. 交互逻辑需兼顾 “输入处理、前置校验、结果反馈”,保证程序的实用性和友好性。
Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐