
【6日でできるJava入門】クイズゲームを完成させる(クラス化)
序章
ここまでのクイズゲームプログラムは、すべての処理がmain()メソッドに直書きされていました。しかし、プログラムが大きくなると保守や拡張が難しくなります。
ここではリファクタリング(=構造の整理・改良)として、「クラス化」を行い、役割ごとに分担することで、読みやすく・修正しやすく・拡張しやすいプログラムへと進化させます。
また、クラス化には以下のメリットがあります。
| 項目 | 内容 |
|---|---|
| 保守性の向上 | クラスごとに役割が明確なので、バグ修正や機能追加が容易 |
| 再利用性の向上 | よく使う処理(問題読み込みや出題処理など)はクラスとして他のプロジェクトでも使いやすい。 |
| 拡張性の向上 | 新ジャンルの追加や機能追加(例:スコアランキング)も最小限の修正で実装できる。 |
| 可読性の向上 | コードが整理され、全体の流れや各部品の関係が把握しやすくなる。 |
ここでは、クイズゲームを次のようなクラス構造に分割します。
- QuizGame(ゲーム全体の管理、mainメソッド担当)
- QuizManager(問題データの管理、出題処理担当)
- QuizGenre(ジャンル情報の管理)
- 必要に応じてユーティリティクラス(例:入力受付)

1.リファクタリングとクラス化
1.1. リファクタリングとは
リファクタリングとは、「動作はそのままで、コードの構造を整理・改善する作業」です。
Javaでは大規模開発や保守のため、クラス化やメソッドの分割によるリファクタリングが不可欠です。
1.2. クラス化のメリット
- 部品化して再利用可能に
- 処理の責任範囲が明確になる。
- 役割ごとに修正や拡張がしやすい。
2.クラス設計と役割
2.1. QuizGameクラス(全体管理・main)
- プログラムの入口
- ジャンル選択やチートモード判定
- ゲーム全体の流れを制御
2.2. QuizManagerクラス(問題読み込み・出題)
- 問題ファイルの読み込み・シャッフル
- 問題の出題・解答受付・採点
- 結果表示
コンストラクタの役割
- 問題ファイルのパスやジャンル名・問題数などを受け取り、内部でデータを初期化
2.3. QuizGenreクラス(ジャンル情報)
- 各ジャンルのファイル名・表示タイトル・問題数などを持つ
- 今後ジャンルを増やす際も管理が楽になる
2.4. 改造のポイント
QuizManagerを用意し、QuizGameからジャンルやチートモードを指定して実行- 入力や選択肢生成、スコア集計などのロジックを各クラスで分担
- 問題ごとに
ArrayListからランダムに出題・選択肢作成 QuizGenreをenumで実装するとさらに拡張しやすい
3.プログラム例
ファイル名:QuizGame.java
import java.util.Scanner;
public class QuizGame {
public static void main(String[] args) throws Exception {
boolean cheatMode = (args.length > 0 && args[0].equals("cheat"));
System.out.println("ジャンルを選んでください:");
System.out.println("1: 県庁所在地クイズ");
System.out.println("2: 世界史クイズ");
System.out.println("3: 日本史クイズ");
System.out.print("==> ");
Scanner sc = new Scanner(System.in);
int genre = 0;
while (true) {
try {
genre = Integer.parseInt(sc.nextLine());
if (genre >= 1 && genre <= 3) break;
System.out.print("1~3の数字で選んでください。\n==> ");
} catch (Exception e) {
System.out.print("数字を入力してください。\n==> ");
}
}
QuizGenre quizGenre = QuizGenre.fromInt(genre);
QuizManager manager = new QuizManager(
quizGenre.getFileName(),
quizGenre.getTitle(),
quizGenre.getQuestionCount(),
cheatMode
);
manager.startQuiz();
}
}ファイル名:QuizGenre.java
public enum QuizGenre {
PREFECTURE(1, "prefectures.txt", "次の都道府県の県庁所在地はどれ?", 10),
WORLD_HISTORY(2, "world_history.txt", "世界史クイズ:次の解答群で正しいものはどれ?", 10),
JAPANESE_HISTORY(3, "japanese_history.txt", "日本史クイズ:次の解答群で正しいものはどれ?", 10);
private int code;
private String fileName;
private String title;
private int questionCount;
QuizGenre(int code, String fileName, String title, int questionCount) {
this.code = code;
this.fileName = fileName;
this.title = title;
this.questionCount = questionCount;
}
public String getFileName() { return fileName; }
public String getTitle() { return title; }
public int getQuestionCount() { return questionCount; }
public static QuizGenre fromInt(int code) {
for (QuizGenre g : QuizGenre.values()) {
if (g.code == code) return g;
}
return PREFECTURE;
}
}ファイル名:QuizManager.java
import java.io.BufferedReader;
import java.io.FileReader;
import java.util.ArrayList;
import java.util.Random;
import java.util.Scanner;
public class QuizManager {
private String fileName;
private String title;
private int questionCount;
private boolean cheatMode;
private ArrayList<String> questions = new ArrayList<String>();
private ArrayList<String> answers = new ArrayList<String>();
public QuizManager(String fileName, String title, int questionCount, boolean cheatMode) throws Exception {
this.fileName = fileName;
this.title = title;
this.questionCount = questionCount;
this.cheatMode = cheatMode;
loadQuestions();
}
private void loadQuestions() throws Exception {
BufferedReader br = new BufferedReader(new FileReader(fileName));
String line;
while ((line = br.readLine()) != null) {
int idx = line.indexOf(',');
questions.add(line.substring(0, idx));
answers.add(line.substring(idx + 1));
}
br.close();
}
public void startQuiz() {
int score = 0;
Random rand = new Random();
Scanner sc = new Scanner(System.in);
ArrayList<String> qs = new ArrayList<>(questions);
ArrayList<String> as = new ArrayList<>(answers);
for (int i = 1; i <= questionCount; i++) {
if (qs.size() == 0) break;
int qIdx = rand.nextInt(qs.size());
String questionText = qs.remove(qIdx);
String correctAns = as.remove(qIdx);
ArrayList<String> choices = new ArrayList<>();
ArrayList<String> backupAnswers = new ArrayList<>(as);
for (int j = 0; j < 3 && backupAnswers.size() > 0; j++) {
int cIdx = rand.nextInt(backupAnswers.size());
choices.add(backupAnswers.remove(cIdx));
}
int insertIdx = rand.nextInt(choices.size() + 1);
choices.add(insertIdx, correctAns);
System.out.println(i + "問目:" + title);
System.out.println("「" + questionText + "」");
for (int j = 0; j < choices.size(); j++) {
System.out.println((j + 1) + ":" + choices.get(j));
}
if (cheatMode) {
System.out.println("【チートモード】正解は「" + correctAns + "」です。");
}
int answer = 0;
while (true) {
System.out.print("==> ");
try {
answer = Integer.parseInt(sc.nextLine());
if (answer >= 1 && answer <= 4) break;
System.out.println("1~4の数字で選んでください。");
} catch (Exception e) {
System.out.println("数字を入力してください。");
}
}
answer--;
if (answer == insertIdx) {
score++;
System.out.println("正解です!");
} else {
System.out.println("不正解。正解は「" + correctAns + "」です。");
}
System.out.println();
}
System.out.println("全" + questionCount + "問終了!あなたの得点は " + score + " 点です。");
}
}4.各クラスの詳細解説
- QuizGame
・mainメソッドで全体の流れを管理
・ジャンル選択やチートモード判定、QuizManagerの生成・実行を行う - QuizGenre(enum)
・各ジャンルの設定をまとめて管理
・拡張しやすく、ジャンル追加も簡単 - QuizManager
・問題・選択肢の生成と出題、採点、スコア表示
・コンストラクタでファイル名・タイトル・問題数・チートモードを受け取る。
・loadQuestions()でデータ読込、startQuiz()で出題・集計
5.コンストラクタについて
QuizManagerのコンストラクタ
- ファイル名・タイトル・出題数・チートモードをパラメータで受け取り、メンバ変数に保存
- インスタンス生成時に自動で
loadQuestions()を呼び出し、データを初期化
6.改造のポイント
- mainメソッドの肥大化を避け、責任を分散
- ジャンル追加時は
QuizGenreに追加するだけでOK - 入力関連や出題ロジックの整理で、保守・拡張性アップ
7.クイズゲームの実行方法
今回のサンプルプログラムでは、「cheat」というキーワードをコマンドライン引数として指定すると、チートモードで動作します。
コマンドラインで実行する場合
引数が「cheat」ならチートモード、それ以外は通常モードで動作します。
# チートモード(正解を表示)
java QuizGame cheat
# 通常モード(正解は表示しない)
java QiuizGameEclipseで実行する場合
メニューの「実行」→「実行構成」をクリックします。

「引数」のタブで、「プログラムの引数」に「cheat」と入力し、「実行」ボタンをクリックします。

実行結果例
ジャンルを選んでください:
1: 県庁所在地クイズ
2: 世界史クイズ
3: 日本史クイズ
==> 3
1問目:日本史クイズ:次の解答群で正しいものはどれ?
「平安京を建設した天皇」
1:藤原定家
2:桓武天皇
3:寛政の改革
4:ポーツマス条約
【チートモード】正解は「桓武天皇」です。
==> 2
正解です!
2問目:日本史クイズ:次の解答群で正しいものはどれ?
「戦国時代を終結させた大名」
1:徳川家康
2:大化
3:伊藤博文
4:板垣退助
【チートモード】正解は「徳川家康」です。
==> まとめ
このようにクラス設計をしっかり行うことで、「長期間の運用」「機能追加」「バグ修正」などがとてもやりやすくなります。Javaの学習や仕事で複雑なプログラムに出会ったときは、「クラス化して整理する」というアプローチが非常に役立ちます。
