案例:外卖系统案例

(一)、核心需求拆解
本次开发的核心目标是:在原有 “外卖菜品上架、浏览” 功能的基础上,新增 “通过菜品唯一 ID 精准查询菜品信息” 的功能,同时保证系统逻辑完整、用户交互友好。拆解为 3 个具体子需求:
(二)、分阶段解题思路
阶段 1:数据模型改造(Food 类)
核心思路:扩展实体属性,保证数据唯一性
- 保留无参构造,满足基础对象创建需求;
- 扩展带参构造,加入id参数,支持创建时直接赋值;
关键考量:
- ID 必须唯一,因此不在用户输入环节手动填写,而是由系统自动分配(避免重复)。
阶段 2:业务逻辑扩展(FoodManager 类)
子步骤 1:设计 ID 自动生成机制
子步骤 2:实现 “ID 查询” 核心逻辑
- 定义Food targetFood = null作为 “匹配结果容器”;
- 增强 for 循环遍历foodList,逐个对比菜品id与用户输入的targetId;
- 找到匹配项时,将菜品对象赋值给targetFood并终止循环(提升效率);
- 若targetFood != null:打印匹配到的菜品详细信息;
- 若targetFood == null:提示 “未找到对应 ID 的菜品”,保证用户体验。
子步骤 3:交互流程完善
阶段 3:程序入口保持稳定(FoodTest 类)
核心思路:开闭原则(对扩展开放、对修改关闭)
(三)、关键设计原则与考量
1. 面向对象核心原则
- 封装性:菜品的id属性私有化,仅通过 Getter/Setter 访问,避免外部直接修改;
- 开闭原则:新增查询功能时,仅扩展代码(新增方法 / 字段),不修改原有 “上架、浏览” 的核心逻辑;
- 单一职责:Food类仅负责封装菜品数据,FoodManager仅负责业务逻辑,FoodTest仅负责启动程序,职责清晰。
2. 业务逻辑合理性
- ID 唯一性保障:系统自动分配 ID,而非用户输入,从源头避免 ID 重复;
- 遍历效率:找到匹配 ID 后立即break终止循环,无需遍历全部列表,提升查询效率;
- 用户体验:
- 查询前校验列表是否为空,避免无效操作;
- 明确的结果反馈(找到 / 未找到),而非无提示或报错。
3. 扩展性预留
- 查询方法独立封装为queryFoodById(),后续可基于此扩展 “ID 修改、ID 删除” 等功能;
- 自增 ID 机制可灵活调整(如从 1000 开始编号,适配实际业务场景)。
(四)、核心思路总结
二、完整代码示例
(一)、菜品类(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. 变量与数据类型
- 整型变量应用:id和nextId使用int类型,适配菜品编号的数值特性;
- 引用类型赋值:通过Food targetFood = null初始化引用变量,接收匹配到的菜品对象,利用 “引用指向对象” 的特性实现结果存储。





