
本文介绍一种面向对象、可维护性强的 java 问答系统设计方法:使用不可变 record 封装题目与选项,分离数据存储(questionsrepository)与交互逻辑(quiz),支持任意数量题目、随机顺序展示及多选反馈。
本文介绍一种面向对象、可维护性强的 java 问答系统设计方法:使用不可变 record 封装题目与选项,分离数据存储(questionsrepository)与交互逻辑(quiz),支持任意数量题目、随机顺序展示及多选反馈。
在开发测验类应用(如小考、知识竞赛或学习工具)时,若采用硬编码多个 new QuestionsLevelEasy(...) 实例并手动管理列表,不仅代码冗余、难以扩展,更违背了单一职责和开闭原则。理想的设计应满足:✅ 题目与答案结构清晰;✅ 支持 N 个选项(非仅单数字答案);✅ 数据加载与用户交互解耦;✅ 易于增删题目,无需修改业务逻辑。
✅ 推荐方案:用 record + 分层架构实现
1. 定义不可变题干模型(Question record)
Java 14+ 引入的 record 是表示只读数据载体的理想选择——自动生成构造器、getter、equals/hashCode/toString,且语义上明确表达“数据不可变”,天然规避并发与误修改风险:
public record Question(String ask, List<string> answers, int solutionOrdinal) {
// solutionOrdinal:正确答案在 answers 列表中的索引(从 1 开始,便于用户输入)
}</string>
⚠️ 注意:solutionOrdinal 使用 1-based 索引(即用户输入 1 表示第一个选项),而非编程常用的 0-based,显著提升用户体验与代码可读性。
2. 隔离数据源:QuestionsRepository
该类唯一职责是提供题目集合,当前以硬编码方式初始化,但未来可无缝替换为 CSV 解析、JSON 加载或数据库查询:
import java.util.List;
public class QuestionsRepository {
public List<question> fetchQuestions() {
return List.of(
new Question("最佳编程语言?",
List.of("C", "C++", "Java"),
3 // Java 是第 3 个选项 → 用户输入 3 即正确
),
new Question("不存在的颜色是?",
List.of("品红(Magenta)", "红色", "蓝色"),
1 // 品红是加色模型中无对应波长的“非光谱色”
),
new Question("推荐的编程字体?",
List.of("Pragmata Pro", "JetBrains Mono", "Comic Sans MS"),
1
)
);
}
}</question>
✅ 优势:新增题目只需在 List.of(...) 中追加一行,零侵入现有逻辑。
分析 Java Spring Boot 后端 Controller 代码,自动生成 Markdown 格式的接口文档。当用户说“生成 Java 接口文档”、“生成 API 文档”、“从 Spring 代码生成文档”时触发。
3. 封装交互流程:Quiz 类
Quiz 不负责生成题目,只专注呈现、接收输入、校验反馈——这是典型的控制反转(IoC) 实践:
import java.util.*;
public class Quiz {
private final List<question> questions;
public Quiz(List<question> questions) {
// 创建可变副本并打乱顺序,再转为不可变视图
List<question> shuffled = new ArrayList(questions);
Collections.shuffle(shuffled);
this.questions = List.copyOf(shuffled); // 安全防御:防止外部修改
}
public void present() {
Scanner scanner = new Scanner(System.in);
System.out.printf("? 本次测验共 %d 题,开始答题!\n", questions.size());
for (int i = 0; i <blockquote>
<p>? 关键设计点:</p>
<ul>
<li>Collections.shuffle() 替代 Random.nextInt() 随机取题,避免重复抽取或遗漏;</li>
<li>List.copyOf() 保证内部状态不可变,增强线程安全与封装性;</li>
<li>输入异常处理(如用户输字母)提升鲁棒性;</li>
<li>输出含题号、明确提示,符合 CLI 友好规范。</li>
</ul>
</blockquote>
<h4>4. 组装运行:App 主类</h4>
<pre class="brush:php;toolbar:false;">public class App {
public static void main(String[] args) {
QuestionsRepository repo = new QuestionsRepository();
Quiz quiz = new Quiz(repo.fetchQuestions());
quiz.present();
}
}
? 总结与进阶建议
| 维度 | 当前实现 | 后续可扩展方向 |
|---|---|---|
| 数据持久化 | 硬编码 List.of(...) | 改为读取 questions.json 或 SQLite |
| 难度分级 | 所有题目混入同一列表 | 在 Question 中增加 enum Level {EASY, MEDIUM, HARD} 字段 |
| 统计反馈 | 仅输出对错 | 记录答对数、耗时、错题重练列表 |
| UI 层 | 控制台(CLI) | 迁移至 JavaFX / Swing / Web(Spring Boot + Thymeleaf) |
? 核心启示:好的类设计不在于功能多,而在于职责纯、边界清、变化少。
将“题目是什么”(data)、“题目从哪来”(repository)、“怎么问用户”(quiz)三者彻底分离,你的代码才能真正支撑 30 题、300 题,甚至 3000 题的平滑演进。
现在,你已掌握构建专业级问答系统的核心范式——简洁、健壮、可生长。下一步,就是把它用在你的项目中,并持续迭代。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










