📖 読了目安 約12分
この記事の目次

Step3: 実装する

実装計画ができたら、いよいよコードを書いていきます。ここでは、初心者が陥りがちな失敗を避け、確実に動くコードを書くための方法を解説します。

なぜ「実装の進め方」が重要なのか

初心者あるある: 「とりあえず全部書いてから動かそう」→ エラーが大量に出て、どこが間違っているか分からない

実装の進め方を間違えると、以下のような問題が起こります:

  • エラーの原因特定が困難: 100行書いてから動かすと、どの行が原因か分からない
  • 手戻りが大きい: 根本的な設計ミスに最後に気づくと、全部書き直し
  • デバッグ時間の増大: 複雑に絡み合ったバグは解決が難しい

正しい進め方を身につけると

  • エラーが出ても原因がすぐ分かる
  • 小さな成功を積み重ねてモチベーションを保てる
  • 確実に動くコードが書ける

原則1: 小さく作って、小さく確認

なぜ小さく作るのか

❌ 悪い例:一気に全部書く
┌─────────────────────────────────────┐
│ 100行のコードを書く                   │
│         ↓                           │
│ 実行してみる                         │
│         ↓                           │
│ エラーが10個出る                      │
│         ↓                           │
│ どこが原因か分からない...             │
└─────────────────────────────────────┘

✅ 良い例:小さく書いて確認を繰り返す
┌─────────────────────────────────────┐
│ 10行書く → 動作確認 → OK!            │
│         ↓                           │
│ 次の10行書く → 動作確認 → OK!        │
│         ↓                           │
│ さらに10行書く → エラー!            │
│         ↓                           │
│ 直前の10行が原因と分かる → すぐ修正   │
└─────────────────────────────────────┘

具体的な進め方

1. 骨格を先に作る

まず、処理の流れ(骨格)だけを作ります。

// 悪い例:いきなり詳細を書き始める
public void processOrder(Order order) {
    // 在庫チェックのロジックをいきなり100行...
}

// 良い例:まず骨格を作る
public void processOrder(Order order) {
    // Step1: 入力値チェック
    validateOrder(order);

    // Step2: 在庫確認
    checkStock(order);

    // Step3: 注文登録
    registerOrder(order);

    // Step4: 通知送信
    sendNotification(order);
}

// 各メソッドは最初は空でOK
private void validateOrder(Order order) {
    // TODO: あとで実装
}

2. 一つずつ肉付けしていく

骨格ができたら、一つずつ中身を実装します。

// Step1から順番に実装
private void validateOrder(Order order) {
    if (order == null) {
        throw new IllegalArgumentException("注文がnullです");
    }
    if (order.getItems().isEmpty()) {
        throw new IllegalArgumentException("注文商品がありません");
    }
    // ここで一度動作確認!
}

3. 一つ実装したら必ず動作確認

実装 → 保存 → 実行 → 確認 → 次へ

このサイクルを短く回すことが大切です。


原則2: 動くコードを維持する

なぜ「常に動く状態」を保つのか

初心者あるある: 「あとでまとめて直すから、今はコンパイルエラーがあってもいいや」

これは危険な考え方です:

  • エラーが雪だるま式に増える: 1つのエラーを放置すると、それに依存する部分もエラーになる
  • 何が正しい状態か分からなくなる: エラーだらけだと、どこまでが動いていたか不明
  • 心理的負担が増大: エラーの山を見るとやる気がなくなる

実践方法

1. コンパイルエラーは即座に直す

コンパイルエラーが出たら、次の行を書く前に直しましょう。

// コンパイルエラーを放置して次に進む ❌
public void method1() {
    unknownMethod();  // エラー!でも後で直そう...
}

public void method2() {
    // method1のエラーを直さないまま次を書く
    // → エラーが増えていく
}

2. TODOコメントで仮実装

まだ実装しない部分は、TODOコメントと仮の実装で動く状態を保ちます。

private int calculateDiscount(Order order) {
    // TODO: 割引計算ロジックを実装
    return 0;  // 仮の値を返して動く状態を維持
}

3. 定期的にコミット

動く状態になったら、こまめにコミットしましょう。

# 機能の一部が動いたらコミット
git add .
git commit -m "注文バリデーション機能を追加"

これにより、いつでも「動いていた状態」に戻れます。


原則3: 既存コードに合わせる

なぜ既存コードに合わせるのか

初心者あるある: 「自分の好きな書き方のほうが読みやすいから」

チーム開発では、個人の好みより一貫性が重要です:

  • 読みやすさ: 統一されたコードは誰が見ても理解しやすい
  • 保守性: スタイルが混在すると、どれが正しいか分からなくなる
  • レビュー効率: 「なぜこの書き方?」という指摘を避けられる

確認すべきポイント

1. 命名規則

// 既存コードがキャメルケースなら
private int orderCount;      // ✅ 既存に合わせる
private int order_count;     // ❌ スネークケースは使わない

// 既存コードがget/setを使っているなら
public int getOrderCount()   // ✅ 既存に合わせる
public int orderCount()      // ❌ 独自スタイルは避ける

2. インデントとフォーマット

// 既存コードがスペース4つなら
if (condition) {
    doSomething();    // ✅ スペース4つ
}

// タブや2スペースは使わない
if (condition) {
  doSomething();      // ❌ スペース2つ
}

3. エラーハンドリング

// 既存コードが例外をスローしているなら
if (order == null) {
    throw new IllegalArgumentException("...");  // ✅
}

// 独自の方法を使わない
if (order == null) {
    return null;  // ❌ 既存と異なる方法
}

4. ログ出力

// 既存コードのログレベルに合わせる
logger.info("注文処理開始: orderId={}", orderId);  // ✅

// 独自のフォーマットを使わない
System.out.println("注文処理開始: " + orderId);    // ❌

既存コードの調べ方

  1. 同じような機能を探す: 「注文処理」を実装するなら、既存の「商品処理」を参考に
  2. コーディング規約を確認: プロジェクトにあれば必ず読む
  3. 先輩に聞く: 「この書き方で合っていますか?」と確認

原則4: YAGNIの原則を守る

YAGNIとは

YAGNI = "You Aren't Gonna Need It"(それは必要にならない)

初心者あるある: 「将来こういう機能が必要になるかも」→ 今は使わない機能を作り込む

なぜYAGNIが重要か

❌ 過剰な実装の問題
┌─────────────────────────────────────┐
│ 今必要な機能: 1つ                    │
│ 「将来のため」に作った機能: 5つ       │
│         ↓                           │
│ ・開発時間が5倍                      │
│ ・将来の仕様変更で作り直し            │
│ ・使われないコードが負債になる        │
└─────────────────────────────────────┘

現実

  • 「将来必要になる」と思った機能の多くは、結局使われない
  • 使われるとしても、想像と違う形で必要になる
  • 今作っても、その時には仕様が変わっている

具体例

// ❌ 過剰な実装
public class OrderProcessor {
    // 今は日本円だけだが、将来のために多通貨対応...
    private Map<String, CurrencyConverter> converters;

    // 今は1店舗だが、将来のためにマルチテナント対応...
    private TenantManager tenantManager;

    // 今は同期処理だが、将来のために非同期対応...
    private AsyncExecutor asyncExecutor;

    // 実際に必要な処理はこれだけ
    public void process(Order order) {
        save(order);
    }
}

// ✅ 必要最小限の実装
public class OrderProcessor {
    public void process(Order order) {
        save(order);
    }
}

YAGNIの実践

  1. 仕様書に書かれていることだけ実装する
  2. 「あると便利」は作らない
  3. 迷ったら作らない
  4. 本当に必要になったら、その時に作る

原則5: 読みやすさを優先する

なぜ読みやすさが重要か

事実: コードは書く時間より読む時間のほうが圧倒的に長い

  • 自分で書いたコードも、1週間後には「他人のコード」
  • チームメンバーがあなたのコードを読む
  • 将来の保守担当者が読む

読みやすいコードの書き方

1. 意味のある名前をつける

// ❌ 分かりにくい名前
int d;  // 何の日数?
String s;  // 何の文字列?
List<Order> list;  // 何のリスト?

// ✅ 意味が分かる名前
int daysSinceLastOrder;
String customerName;
List<Order> pendingOrders;

2. 1つのメソッドは1つのことだけ

// ❌ 1つのメソッドで複数のことをやる
public void processOrder(Order order) {
    // バリデーション(30行)
    // 在庫チェック(20行)
    // 注文登録(25行)
    // メール送信(15行)
    // ログ出力(10行)
    // 合計100行...
}

// ✅ 役割ごとにメソッドを分ける
public void processOrder(Order order) {
    validateOrder(order);
    checkStock(order);
    registerOrder(order);
    sendConfirmationEmail(order);
}

3. ネストを浅くする

// ❌ 深いネスト
public void process(Order order) {
    if (order != null) {
        if (order.getItems() != null) {
            if (!order.getItems().isEmpty()) {
                for (Item item : order.getItems()) {
                    if (item.getStock() > 0) {
                        // 処理...
                    }
                }
            }
        }
    }
}

// ✅ 早期リターンでネストを減らす
public void process(Order order) {
    if (order == null) return;
    if (order.getItems() == null) return;
    if (order.getItems().isEmpty()) return;

    for (Item item : order.getItems()) {
        if (item.getStock() <= 0) continue;
        // 処理...
    }
}

4. マジックナンバーを避ける

// ❌ 意味不明な数値
if (status == 3) {
    discount = price * 0.1;
}

// ✅ 定数で意味を明確に
private static final int STATUS_PREMIUM = 3;
private static final double PREMIUM_DISCOUNT_RATE = 0.1;

if (status == STATUS_PREMIUM) {
    discount = price * PREMIUM_DISCOUNT_RATE;
}

実践チェックリスト

実装中は、以下を意識しましょう:

進め方

  • [ ] 10〜20行書いたら動作確認している
  • [ ] コンパイルエラーを放置していない
  • [ ] 動く状態を常に維持している

コードの品質

  • [ ] 既存コードのスタイルに合わせている
  • [ ] 仕様書にないことは実装していない(YAGNI)
  • [ ] 意味のある名前をつけている
  • [ ] 1メソッドが長すぎない(目安: 30行以内)

確認

  • [ ] 定期的にコミットしている
  • [ ] 動作確認してから次に進んでいる

次のステップ

実装が一段落したら、次は動作確認です。

Step4: 動作確認する


関連ページ