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); // ❌
既存コードの調べ方
- 同じような機能を探す:「注文処理」を実装するなら、既存の「商品処理」を参考に
- コーディング規約を確認:プロジェクトにあれば必ず読む
- 先輩に聞く:「この書き方で合っていますか?」と確認
原則4: YAGNIの原則を守る
YAGNIとは
YAGNI = "You Aren't Gonna Need It"(それは必要にならない)
初心者あるある
「将来こういう機能が必要になるかも」→ 今は使わない機能を作り込む
なぜYAGNIが重要か
- ❌ 過剰な実装の問題
今必要な機能: 1つ → 「将来のため」に作った機能: 5つ → 開発時間が5倍 → 将来の仕様変更で作り直し → 使われないコードが負債になる - ⭕ 必要な分だけ作る
今必要な機能だけを実装 → 必要になったらそのときに追加 → シンプルで保守性が高い
現実:
- 「将来必要になる」と思った機能の多くは、結局使われない
- 使われるとしても、想像と違う形で必要になる
- 今作っても、その時には仕様が変わっている
具体例
// ❌ 過剰な実装
public class OrderProcessor {
// 今は日本円だけだが、将来のために多通貨対応...
private Map 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の実践
- 仕様書に書かれていることだけ実装する
- 「あると便利」は作らない
- 迷ったら作らない
- 本当に必要になったら、その時に作る
原則5: 読みやすさを優先する
なぜ読みやすさが重要か
事実:コードは書く時間より読む時間のほうが圧倒的に長い
- 自分で書いたコードも、1週間後には「他人のコード」
- チームメンバーがあなたのコードを読む
- 将来の保守担当者が読む
読みやすいコードの書き方
1. 意味のある名前をつける
// ❌ 分かりにくい名前
int d; // 何の日数?
String s; // 何の文字列?
List list; // 何のリスト?
// ✅ 意味が分かる名前
int daysSinceLastOrder;
String customerName;
List 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行以内)
確認
- ☐ 定期的にコミットしている
- ☐ 動作確認してから次に進んでいる
次のステップ
実装が一段落したら、次は動作確認です。