赛百味复刻项目中订单配料有效性与顺序验证的代码优化方案咨询
我完全懂你这种看着一堆嵌套条件判断越写越头大的感觉——面条代码不仅难维护,后续要加新规则或者调整配料顺序简直是噩梦。咱们来把这个验证逻辑拆得更清晰、更易扩展,同时保留你用HashSet提升查找效率的优点。
核心思路:用「阶段化状态机」替代嵌套判断
你的订单规则是严格按顺序分阶段的,刚好可以把每个阶段的要求(必选/可选、数量范围、对应菜单分类)抽象出来,然后用线性遍历的方式逐个阶段验证,避免嵌套。
第一步:把规则集中管理
先把每个阶段的规则定义成枚举,这样后续改规则(比如调整配料数量上限)直接改枚举就行,不用动验证逻辑:
enum IngredientStage { BREAD(1, 1, "Bread"), // 必选1份,对应菜单的Bread分类 MEAT(0, 1, "Meat"), // 可选0-1份,对应菜单的Meat分类 CHEESE(1, 1, "Cheese"), // 必选1份,对应菜单的Cheese分类 EXTRAS(1, 3, "Extras"), // 必选1-3份,对应菜单的Extras分类 SAUCES(1, 3, "Sauces"); // 必选1-3份,对应菜单的Sauces分类 private final int minRequired; private final int maxAllowed; private final String menuCategoryKey; IngredientStage(int min, int max, String menuKey) { this.minRequired = min; this.maxAllowed = max; this.menuCategoryKey = menuKey; } // Getter方法 public int getMinRequired() { return minRequired; } public int getMaxAllowed() { return maxAllowed; } public String getMenuCategoryKey() { return menuCategoryKey; } }
第二步:实现线性的阶段验证逻辑
遍历订单配料时,我们只需要跟踪当前处于哪个阶段、当前阶段已选了多少配料,遇到不属于当前阶段的配料时,先检查当前阶段是否满足最低要求,再切换到下一个阶段验证:
boolean isValid(List<String> ingredients) { // 先校验总数量的边界值 if (ingredients.size() < 4 || ingredients.size() > 9) { return false; } IngredientStage[] stages = IngredientStage.values(); int currentStageIdx = 0; int countInCurrentStage = 0; for (String ingredient : ingredients) { IngredientStage currentStage = stages[currentStageIdx]; Set<String> validItems = Restaurant.menu.get(currentStage.getMenuCategoryKey()); // 当前配料属于当前阶段的合法选项 if (validItems.contains(ingredient)) { countInCurrentStage++; // 检查是否超过当前阶段的最大允许数量 if (countInCurrentStage > currentStage.getMaxAllowed()) { System.out.println("错误:" + currentStage.name().toLowerCase() + "数量超出上限"); return false; } } else { // 不属于当前阶段,尝试切换到下一个阶段 currentStageIdx++; // 先检查当前阶段是否满足最低要求 if (currentStageIdx >= stages.length || countInCurrentStage < currentStage.getMinRequired()) { System.out.println("错误:" + currentStage.name().toLowerCase() + "数量未达要求"); return false; } // 验证新阶段是否包含该配料 IngredientStage nextStage = stages[currentStageIdx]; validItems = Restaurant.menu.get(nextStage.getMenuCategoryKey()); if (!validItems.contains(ingredient)) { System.out.println("错误:非法配料 " + ingredient); return false; } // 新阶段计数从1开始 countInCurrentStage = 1; } } // 最后检查最后一个阶段是否满足最低要求 IngredientStage lastStage = stages[currentStageIdx]; if (countInCurrentStage < lastStage.getMinRequired()) { System.out.println("错误:" + lastStage.name().toLowerCase() + "数量未达要求"); return false; } // 确保所有阶段都按顺序走完(比如不能只选到Extras就结束,必须到Sauces) if (currentStageIdx != stages.length - 1) { System.out.println("错误:缺少必填阶段 " + stages[currentStageIdx + 1].name().toLowerCase()); return false; } return true; }
额外优化建议
- 替换打印为自定义异常:把
System.out.println换成自定义的InvalidOrderException,携带具体错误信息,这样上层代码可以更灵活地处理错误(比如返回友好提示给用户)。 - 提取辅助方法:把“检查配料是否属于某个菜单分类”提取成
private boolean isIngredientValid(String ingredient, String category),减少重复代码,提升可读性。 - 添加空指针防护:确保
Restaurant.menu的每个分类都提前初始化了HashSet,避免get()返回null导致空指针。 - 补充单元测试:针对每个阶段的边界情况(比如跳过肉类阶段、刚好选3份Extras、总数量刚好4/9等)写单元测试,确保逻辑稳定。
另外,你选择用HashSet存储菜单的思路非常正确——O(1)的查找效率比ArrayList的O(n)高效多了,完全不用改这部分。
内容的提问来源于stack exchange,提问作者Deutrys
相关产品推荐
相关产品推荐

