3日目のゴール
3日目のテーマは
「同じ“追加・一覧・削除”でも、設計とコードの読みやすさをもう一段引き上げること」 です。
機能は増やしません。
でも、こういうところを意識して手を入れます。
クラス同士の役割を、もっとはっきり分ける
List の扱い方を「パターン」として体に入れる
入力処理を“雑用”ではなく“設計の一部”として見る
「動けばいい」から一歩抜けて、
「読めば分かる」「直せそう」と思えるコードに近づけます。
まず“今の設計”を言葉で整理する
2日目までの構成を振り返る
ここまでで、だいたいこんな構成になっています。
Task
TaskManager
InputHelper
Main
役割はこうでした。
Task は「1件分のタスク」
TaskManager は「タスクの List と ID 採番」
InputHelper は「入力の補助」
Main は「メニューとアプリの流れ」
3日目では、ここに少しだけ“設計の視点”を足します。
Task は「データ+ちょっとした振る舞い」
TaskManager は「ビジネスロジック(タスク操作)」
InputHelper は「入出力の窓口の一部」
Main は「アプリの“司会進行”だけ」
このイメージで、コードを整えていきます。
Task を“ただの入れ物”から一歩進める
表示ロジックを少しだけリッチにする
Task はすでに id, title, done を持っています。
ここに「自分を文字列として表現する」メソッドを足します。
public class Task {
int id;
String title;
boolean done;
Task(int id, String title) {
if (title == null || title.isEmpty()) {
throw new IllegalArgumentException("タイトルは必須です。");
}
this.id = id;
this.title = title;
this.done = false;
}
void markDone() {
this.done = true;
}
String toDisplayString(int index) {
String status = done ? "[x]" : "[ ]";
return index + ": " + status + " (id=" + id + ") " + title;
}
}
Javaここでのポイントは、
表示の組み立てを Task 側に寄せた
TaskManager は「表示して」と頼むだけにできる
ということです。
「自分の見せ方」は、できるだけそのクラスの中に閉じ込めると、
あとで表示形式を変えたくなったときに楽になります。
TaskManager に“責務の名前”をつけていく
「一覧を表示する」ではなく「一覧を“出力する文字列”にする」
2日目までは、TaskManager が System.out.println を直接呼んでいました。
3日目では、「表示する文字列を作る」ところまでを TaskManager に任せて、
「どこに出すか」は Main に任せる形にしてみます。
import java.util.ArrayList;
public class TaskManager {
ArrayList<Task> tasks;
int nextId;
TaskManager() {
tasks = new ArrayList<>();
nextId = 1;
}
Task addTask(String title) {
Task task = new Task(nextId, title);
tasks.add(task);
nextId++;
return task;
}
boolean isEmpty() {
return tasks.isEmpty();
}
ArrayList<String> buildTaskLines() {
ArrayList<String> lines = new ArrayList<>();
for (int i = 0; i < tasks.size(); i++) {
Task t = tasks.get(i);
lines.add(t.toDisplayString(i));
}
return lines;
}
int findIndexById(int id) {
for (int i = 0; i < tasks.size(); i++) {
if (tasks.get(i).id == id) {
return i;
}
}
return -1;
}
Task removeTaskById(int id) {
int index = findIndexById(id);
if (index == -1) {
return null;
}
return tasks.remove(index);
}
}
Javaここでの重要ポイントは、
TaskManager は「タスクをどう扱うか」だけに集中している
「どこに表示するか」「どうメッセージを出すか」は知らない
という状態にしたことです。
これが「責務を分ける」ということの、かなり実践的な形です。
入力処理を“アプリの会話”として見る
InputHelper に「メニュー選択」を任せる
2日目の InputHelper は「文字列」「整数」を読むだけでした。
3日目では、「メニュー番号を読む」という“意味のある入力”を足します。
import java.util.Scanner;
public class InputHelper {
Scanner scanner;
InputHelper(Scanner scanner) {
this.scanner = scanner;
}
String readLine(String prompt) {
System.out.print(prompt);
return scanner.nextLine();
}
Integer readInt(String prompt) {
System.out.print(prompt);
String line = scanner.nextLine();
if (line == null || line.isEmpty()) {
System.out.println("何も入力されていません。");
return null;
}
try {
return Integer.parseInt(line);
} catch (NumberFormatException e) {
System.out.println("整数で入力してください。");
return null;
}
}
String readMenuChoice() {
System.out.println();
System.out.println("=== ミニToDoアプリ ===");
System.out.println("1: タスク追加");
System.out.println("2: タスク一覧");
System.out.println("3: タスク削除(ID指定)");
System.out.println("0: 終了");
System.out.print("番号を選んでください: ");
return scanner.nextLine();
}
}
Javaここでの狙いは、
Main から「メニューの見た目」を追い出す
Main は「選ばれた結果」だけを見て分岐する
という形にすることです。
3日目版 Main:かなり“司会進行”っぽくなる
import java.util.ArrayList;
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
TaskManager manager = new TaskManager();
Scanner scanner = new Scanner(System.in);
InputHelper input = new InputHelper(scanner);
while (true) {
String choice = input.readMenuChoice();
if ("0".equals(choice)) {
System.out.println("終了します。");
break;
} else if ("1".equals(choice)) {
handleAddTask(manager, input);
} else if ("2".equals(choice)) {
handleShowTasks(manager);
} else if ("3".equals(choice)) {
handleRemoveTask(manager, input);
} else {
System.out.println("不正な入力です。もう一度選んでください。");
}
}
scanner.close();
}
static void handleAddTask(TaskManager manager, InputHelper input) {
String title = input.readLine("タスクのタイトルを入力してください: ");
if (title == null || title.isEmpty()) {
System.out.println("タイトルが空です。追加を中止します。");
return;
}
try {
Task task = manager.addTask(title);
System.out.println("タスクを追加しました: id=" + task.id + " / " + task.title);
} catch (IllegalArgumentException e) {
System.out.println("タスクの追加に失敗しました: " + e.getMessage());
}
}
static void handleShowTasks(TaskManager manager) {
System.out.println("=== タスク一覧 ===");
if (manager.isEmpty()) {
System.out.println("タスクはありません。");
return;
}
ArrayList<String> lines = manager.buildTaskLines();
for (String line : lines) {
System.out.println(line);
}
}
static void handleRemoveTask(TaskManager manager, InputHelper input) {
handleShowTasks(manager);
if (manager.isEmpty()) {
return;
}
Integer id = input.readInt("削除したいタスクのIDを入力してください: ");
if (id == null) {
System.out.println("削除を中止します。");
return;
}
Task removed = manager.removeTaskById(id);
if (removed == null) {
System.out.println("そのIDのタスクは存在しません: " + id);
} else {
System.out.println("タスクを削除しました: id=" + removed.id + " / " + removed.title);
}
}
}
Javaここで注目してほしいのは、
Main のメソッド名が「やりたいこと」をそのまま表しているhandleAddTask / handleShowTasks / handleRemoveTask
ということです。
中身を読まなくても、
「このアプリが何をしているか」が、メソッド名だけで伝わります。
List 管理の“パターン”を言葉にしておく
3日目までで、List に対してやっていることはほぼ出そろいました。
可変長の集まりとして ArrayList<T> を使うadd で末尾に追加するget(i) で i 番目を取り出すsize() で要素数を知るisEmpty() で空かどうかを見るremove(i) で i 番目を削除する
for 文で 0〜size-1 を回して一覧を作る
そして、ID などの「別の識別子」で削除したいときは、
「IDからインデックスを探す」メソッドを用意する
そのインデックスを使って remove(index) する
という二段構えにする。
これが、
「List を使った管理の基本パターン」
として、かなり完成形に近いです。
入力処理の“パターン”も言葉にしておく
入力についても、パターンが見えてきました。
入力はまず「文字列」として受け取る
「どう解釈するか」は自分で決める(整数にする/そのまま使う)
変な入力(空文字・数字以外)が来る前提で書く
変な入力が来てもアプリを落とさず、メッセージを出して続行する
そして、
「よくやる入力」は小さなメソッドに切り出す。
メニュー選択
整数入力
1行のテキスト入力
これらを InputHelper にまとめることで、
Main のコードが「会話の流れ」に集中できるようになります。
3日目で絶対に押さえてほしい本質
今日いちばん大事なのは、
「同じ機能でも、“責務を分けて名前をつける”だけで、コードが一気に理解しやすくなる」
という感覚です。
Task は「タスク1件の表現と見せ方」
TaskManager は「タスクの追加・検索・削除というロジック」
InputHelper は「ユーザーとの入力の窓口」
Main は「アプリ全体の流れと、各処理の呼び出し」
そして、List と入力処理には、
もう「自分の中の定番パターン」ができつつあります。
ここまで来ると、
機能を増やすときにやることはシンプルです。
「この機能は、どのクラスの責務か?」
「List に対して、どのパターンを使えばいいか?」
「入力は、どんな形で受け取るのが自然か?」
この3つを考えれば、
自然とコードの形が見えてきます。
もし余力があれば、
今日の設計のまま「完了フラグを切り替えるメニュー」を足してみてください。
ID から Task を探すmarkDone() を呼ぶ
一覧表示で [x] がつく
ここまでできたら、
あなたの ToDo アプリは、もう「自分で設計した」と胸を張って言えるレベルになっています。

