実務風メニュー設計(Main+Service構造)
では、先ほどの Stream API メニュー を、実務風に Mainクラス+Serviceクラス構造 に整理してみます。
こうすると、プログラムが拡張しやすく、メンテナンスもしやすくなります。
1. 構造イメージ
MenuAppMain.java ← mainメソッド+アプリ起動処理
MenuService.java ← メニュー処理をまとめたサービスクラス
- Main: 入力受付・ループ制御
- Service: 実際のメニュー処理をまとめる
- Map+Stream API で柔軟に分岐
2. MenuService.java
import java.util.Scanner;
import java.util.Map;
import java.util.Optional;
public class MenuService {
private final Scanner scanner;
private final Map<Integer, Runnable> menuMap;
public MenuService(Scanner scanner) {
this.scanner = scanner;
// メニュー番号と処理をマッピング
this.menuMap = Map.of(
1, this::showProducts,
2, this::searchProduct,
3, this::purchase,
0, this::exitApp
);
}
// メニューを表示
public void showMenu() {
System.out.println("=== メニュー ===");
System.out.println("1: 商品一覧を見る");
System.out.println("2: 商品を検索する");
System.out.println("3: 購入手続きへ進む");
System.out.println("0: 終了する");
System.out.print("番号を選んでください: ");
}
// 入力値に応じて処理を実行
public boolean executeChoice(int choice) {
Optional.ofNullable(menuMap.get(choice))
.ifPresentOrElse(
Runnable::run,
() -> System.out.println("→ 無効な番号です。\n")
);
return choice != 0; // 0なら終了
}
// メニュー処理
private void showProducts() {
System.out.println("→ 商品一覧を表示します。\n");
}
private void searchProduct() {
System.out.print("検索したい商品名を入力してください: ");
String keyword = scanner.nextLine();
System.out.println("→ 「" + keyword + "」を検索しました。\n");
}
private void purchase() {
System.out.println("→ 購入手続きに進みます。\n");
}
private void exitApp() {
System.out.println("→ プログラムを終了します。");
}
}
Java3. MenuAppMain.java
import java.util.Scanner;
public class MenuAppMain {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
MenuService service = new MenuService(scanner);
boolean running = true;
while (running) {
service.showMenu();
int choice = -1;
try {
choice = Integer.parseInt(scanner.nextLine());
} catch (NumberFormatException e) {
System.out.println("→ 数字を入力してください。\n");
continue;
}
running = service.executeChoice(choice);
}
scanner.close();
}
}
Java4. この設計のメリット
| 特徴 | 解説 |
|---|---|
| Mainは入力・ループ制御だけ | メニュー処理の詳細は Service にまとめて可読性アップ |
| ServiceにMap+ラムダで処理を登録 | メニュー追加・削除が簡単 |
| Stream API (Optional) で安全に呼び出し | 無効入力時の null チェック不要 |
| 拡張性が高い | 例えばGUIやWebに置き換えるときも、Service クラスを使い回せる |
5. 発展アイデア(実務応用)
- ログ出力
- 実務では
System.out.printlnの代わりに SLF4J や Logback を使う
- 実務では
- 複数サービスの統合
ProductService、UserServiceなどに処理を分け、MenuServiceは橋渡し役だけにする
- ユーザー入力のバリデーション
- try-catch + 正規表現で不正な入力を安全に弾く
- Unit Test しやすい設計
- メニュー処理を Service にまとめることで、Main を起動せずに単体テストが可能
まとめ
- Main+Service構造にすると、プログラムが読みやすく、拡張しやすくなる
- Stream API+Map+ラムダで switch 文を置き換えると、分岐の増減が簡単
- 実務では、こうした設計パターンがベースになります
