如何为含多选项的选择器类传递选项代码并迁移Switch逻辑
为菜单选项绑定独立逻辑,消除Switch语句的方案
现有问题与需求
你目前实现了一个MenuConstructor类用于生成控制台菜单,但每次使用都需要在外部编写switch语句处理选项逻辑,希望改成给每个选项单独绑定逻辑,调用runMenu即可自动执行对应动作,彻底摆脱重复的switch代码。
现有实现代码:
package lanta.utils; import java.util.HashMap; import java.util.Scanner; public class MenuConstructor { public HashMap<Integer, String> options; public int option; public MenuConstructor(String defaultOption,String... options) { this.options = new HashMap<>(); this.options.put(0, defaultOption); this.option = 1; for (String option : options) { this.options.put(this.option, option); this.option++; } } public MenuConstructor(HashMap<Integer, String> options) { this.options = options; } public int getOption(Scanner scanner) { StringBuilder message = new StringBuilder(); message.append(0 + " - ").append(this.options.get(0)); for(int i = 1; i < this.options.size(); i++){ message.append(" | ").append(i).append(" - ").append(this.options.get(i)); } System.out.println(message); System.out.println("Select an option:"); String selection = scanner.nextLine().replaceAll("\\D.*", ""); try { int selectedOption = Integer.parseInt(selection); if(options.containsKey(selectedOption)) return selectedOption; else { System.out.println("Invalid option, defaulting to 0"); return 0; } } catch (NumberFormatException e) { System.out.println("Invalid number format, defaulting to 0"); return 0; } } }
你期望的调用方式(调整为可运行的Java代码形式):
Scanner scanner = new Scanner(System.in); MenuConstructor menu = new MenuConstructor("Exit", "Say hello world", "Create a student"); // 绑定选项0的逻辑 menu.addAction(0, () -> { System.out.println("退出菜单"); return -1; // 返回-1表示退出循环 }); // 绑定选项1的逻辑 menu.addAction(1, () -> { System.out.println("Hello world"); return 0; // 返回0表示继续菜单循环 }); // 绑定选项2的逻辑 menu.addAction(2, () -> { Student student = new Student(); System.out.println("学生对象已创建"); return 0; }); menu.runMenu(scanner);
解决方案:用函数式接口封装选项逻辑
核心思路是通过函数式接口将每个选项的逻辑封装成独立的动作,存储在菜单类中,无需外部switch语句,直接通过选项索引匹配并执行对应动作。
步骤1:定义函数式接口
先定义一个函数式接口,用来表示菜单选项的执行动作,返回值用于控制菜单循环:
@FunctionalInterface public interface MenuAction { // 返回值规则:-1表示退出菜单,0表示继续显示菜单 int execute(); }
步骤2:修改MenuConstructor类
新增存储动作的Map,添加绑定动作的方法,以及自动运行菜单的循环方法:
package lanta.utils; import java.util.HashMap; import java.util.Scanner; public class MenuConstructor { // 存储选项文本 private HashMap<Integer, String> optionTexts; // 存储选项对应的执行动作 private HashMap<Integer, MenuAction> optionActions; public MenuConstructor(String defaultOption, String... options) { this.optionTexts = new HashMap<>(); this.optionActions = new HashMap<>(); // 初始化默认选项(索引0) this.optionTexts.put(0, defaultOption); int index = 1; for (String option : options) { this.optionTexts.put(index, option); index++; } } public MenuConstructor(HashMap<Integer, String> optionTexts) { this.optionTexts = optionTexts; this.optionActions = new HashMap<>(); } // 为指定选项绑定执行动作 public void addAction(int optionIndex, MenuAction action) { if (optionTexts.containsKey(optionIndex)) { this.optionActions.put(optionIndex, action); } else { throw new IllegalArgumentException("不存在索引为" + optionIndex + "的选项"); } } // 获取用户选择的选项(私有方法,内部调用) private int getSelectedOption(Scanner scanner) { StringBuilder message = new StringBuilder(); message.append(0).append(" - ").append(this.optionTexts.get(0)); for (int i = 1; i < this.optionTexts.size(); i++) { message.append(" | ").append(i).append(" - ").append(this.optionTexts.get(i)); } System.out.println(message); System.out.println("Select an option:"); String selection = scanner.nextLine().replaceAll("\\D.*", ""); try { int selectedOption = Integer.parseInt(selection); if (optionTexts.containsKey(selectedOption)) { return selectedOption; } else { System.out.println("无效选项,默认选择0"); return 0; } } catch (NumberFormatException e) { System.out.println("输入格式错误,默认选择0"); return 0; } } // 启动菜单循环 public void runMenu(Scanner scanner) { while (true) { int selected = getSelectedOption(scanner); // 若未绑定动作,默认执行空动作(继续循环) MenuAction action = optionActions.getOrDefault(selected, () -> 0); int result = action.execute(); // 返回-1则退出菜单循环 if (result == -1) { break; } } scanner.close(); } }
步骤3:完整调用示例
现在可以按照你期望的方式调用,以下是可直接运行的示例代码:
import lanta.utils.MenuConstructor; import java.util.Scanner; public class MenuDemo { public static void main(String[] args) { Scanner scanner = new Scanner(System.in); MenuConstructor menu = new MenuConstructor("Exit", "Say hello world", "Create a student"); // 绑定选项0(退出)的逻辑 menu.addAction(0, () -> { System.out.println("菜单已退出"); return -1; }); // 绑定选项1的逻辑 menu.addAction(1, () -> { System.out.println("Hello world"); return 0; }); // 绑定选项2的逻辑 menu.addAction(2, () -> { Student student = new Student(); System.out.println("成功创建学生对象"); return 0; }); // 启动菜单 menu.runMenu(scanner); } // 示例Student类 static class Student { // 可添加类属性与方法 } }
思路说明
- 用
MenuAction函数式接口将每个选项的逻辑封装为独立的Lambda表达式,彻底消除了冗余的switch语句。 - 菜单类专注于显示选项、获取输入、执行动作,外部只需绑定逻辑即可,符合单一职责原则。
- 新增选项时,仅需添加文本并绑定对应动作,无需修改菜单类核心逻辑,符合开闭原则。
内容的提问来源于stack exchange,提问作者DuskFlare13
相关产品推荐
相关产品推荐

