
本文介绍如何将世界杯分组对阵逻辑封装为独立、可复用的工具类,并通过简洁的用户输入机制(如按任意键/字符触发)控制比赛轮次启动,避免业务逻辑与主流程耦合。
本文介绍如何将世界杯分组对阵逻辑封装为独立、可复用的工具类,并通过简洁的用户输入机制(如按任意键/字符触发)控制比赛轮次启动,避免业务逻辑与主流程耦合。
在构建类似世界杯模拟程序时,良好的面向对象设计要求职责分离:Teams 类应专注管理分组数据与展示逻辑,而用户交互(如“按回车开始本轮”)应由专用的输入控制器承担,而非硬编码在 main() 中。下面我们将重构原有代码,实现两个核心目标:
- 增强 Teams 类的内聚性——统一管理所有分组数据,支持按名称获取队伍列表;
- 新增 InputController 工具类——封装标准输入逻辑,提供 waitForRoundStart() 方法,以字符输入(如 'y' 或任意键)作为回合启动信号。
✅ 重构后的 Teams.java(静态工厂 + 分组抽象)
import java.util.*;
public class Teams {
private static final Map<String, List<String>> GROUPS = new HashMap<>();
static {
GROUPS.put("A", Arrays.asList("Argentina", "Portugal", "England", "Germany"));
GROUPS.put("B", Arrays.asList("United States", "France", "Spain", "Japan"));
GROUPS.put("C", Arrays.asList("Canada", "Mexico", "Poland", "South Korea"));
GROUPS.put("D", Arrays.asList("Uruguay", "Wales", "Switzerland", "Croatia"));
}
public static List<String> getGroup(String groupName) {
return Optional.ofNullable(GROUPS.get(groupName.toUpperCase()))
.orElseThrow(() -> new IllegalArgumentException("Unknown group: " + groupName));
}
public static void printGroup(String groupName) {
List<String> teams = getGroup(groupName);
System.out.println("Group " + groupName + ":");
System.out.printf("%s vs. %s%n", teams.get(3), teams.get(0));
System.out.printf("%s vs. %s%n", teams.get(2), teams.get(1));
System.out.println("///////////////////////////////////////////");
}
}✅ 新增 InputController.java(专注用户输入控制)
import java.util.Scanner;
public class InputController {
private static final Scanner scanner = new Scanner(System.in);
/**
* 阻塞等待用户输入一个字符(回车即确认),用于启动单轮比赛
* @param roundName 轮次名称(如 "Round of 16")
*/
public static void waitForRoundStart(String roundName) {
System.out.print("▶ Press any key + Enter to begin " + roundName + "...");
scanner.nextLine(); // 消耗换行符(兼容前序输出)
System.out.println("✅ " + roundName + " started!\n");
}
/**
* 可选:支持严格字符校验(如仅接受 'y'/'Y' 确认)
*/
public static boolean confirmWithChar(String prompt, char expected) {
System.out.print(prompt + " (press '" + expected + "' to continue): ");
String input = scanner.nextLine().trim();
return !input.isEmpty() && Character.toLowerCase(input.charAt(0)) == Character.toLowerCase(expected);
}
}✅ 主程序 WorldCupMain.java —— 清晰编排流程
public class WorldCupMain {
public static void main(String[] args) {
System.out.println("Welcome to the FIFA 2023 World Cup\n");
// 第一阶段:小组赛对阵展示(不需用户干预)
System.out.println("? Group Stage Matchups:");
Teams.printGroup("A");
Teams.printGroup("B");
Teams.printGroup("C");
Teams.printGroup("D");
// 第二阶段:淘汰赛启动控制(显式用户交互)
System.out.println("\n? Round of 16 is next!");
InputController.waitForRoundStart("Round of 16");
// 此处可扩展:加载晋级队伍、生成对阵表、模拟比赛等
System.out.println("? Simulating matches... (logic to be implemented)");
}
}⚠️ 注意事项与最佳实践
- 资源安全:Scanner 实例在 InputController 中声明为 static 是合理的(单例输入源),但生产环境建议在应用生命周期结束时调用 scanner.close()。
- 输入健壮性:waitForRoundStart() 使用 nextLine() 可安全处理空输入和残留换行符;若需更严格的交互(如重试机制),可封装循环逻辑。
- 可扩展性:Teams 类采用 Map 存储分组,未来新增分组只需修改静态块,无需改动方法签名。
- 测试友好:所有核心逻辑(分组数据、输入等待)已解耦,便于单元测试(例如用 System.setIn() 注入模拟输入)。
通过以上设计,程序结构清晰、职责分明,既满足“按提示启动轮次”的需求,又为后续实现淘汰赛逻辑、比分录入、胜负判定等扩展功能打下坚实基础。


















