書籍概要

WEB+DB PRESS plus

[増補改訂]良いコードを書く技術
──読みやすく保守しやすいプログラミング作法

著者
発売日
更新日

概要

読みやすく保守しやすい「良いコード」の書き方を解説した入門書です。本書を読むと,良いコードを書くための習慣から,名前の付け方,コードの分割や集約を行う方法,抽象化の作法,計算量とアルゴリズム,ユニットテストやメタプログラミング,そして簡単なフレームワークの自作まで,プログラマーとして長く役立つ基本が身に付きます。

2011年に刊行し,大好評を博した初版を,10年ぶりに改訂しました。改訂版では,コード例をモダン化したほか,第7章「データ構造」を新たに書き下ろしました。10年ぶりの改訂であるにも関わらず,本書の根幹は驚くほど変わっていません。それはすなわち,基礎や基本といった本質的な知識は,陳腐化しないということです。

こんな方におすすめ

  • 良いコードを書きたいけれども,何から手を付けていいかわからない方
  • 良い仕事をしたい初級から中級のプログラマーの方
  • 読みやすく保守しやすいコードを書きたい方
  • 新人に良いコードとは何かを知ってほしい先輩・上司・達人プログラマーの方

サンプル

samplesamplesamplesamplesample

目次

    • はじめに
    • 謝辞
    • 増補改訂での更新点
    • コード例について
    • サポートページ

第1章 良いコードとは何か

1.1
良いコードの定義と価値

1.2
良いコードの定義

  • 保守性が高い
  • すばやく効率的に動作する
  • 正確に動作する
  • 無駄な部分がない

1.3
良いコードの価値

  • プロジェクトを強力に推し進める
  • プログラマーとしての評価が高まる
  • 仕事に満足感や自信が持てるようになる

1.4
代表者の声

  • 良い仕事をしたい普通のプログラマー
  • 達人プログラマーを目指す中級プログラマー
  • 達人プログラマー

1.5
まとめ

第2章 良いコードを書くための5つの習慣

2.1
良いコードは1日にしてならず

2.2
習慣その1 読む ── コードを読んで読んで,読みまくれ!

  • Column:GitHubでコードの海を泳ぐ

2.3
習慣その2 書く ── とにかくコードを書こう

  • Column:1人でプログラムを書けますか?

2.4
習慣その3 道具を磨く ── 使う道具は常に磨いておこう

  • エディタ/統合開発環境
  • 自動化
  • バージョン管理ツール
  • UNIX/Linux/macOS
  • Column:良い道具に乗り換える

2.5
習慣その4 知る ── 良い知識を得よう

  • 書籍 ── 原典とHow To本の2冊買いがお勧め
  • リファレンスや仕様書などのドキュメント
  • Webサイト

2.6
習慣その5 聞く ── アウトプットと人からのフィードバックでさらなる成長を

  • コードレビューを受ける
  • ブログを書く
  • コミュニティや勉強会に参加する
  • 成果を発表する

2.7
まとめ

第3章 名前付け

3.1
良いコードは良い名前から生まれる

3.2
代表者の声

  • 良い仕事をしたい普通のプログラマー
  • 達人プログラマーを目指す中級プログラマー
  • 達人プログラマー

3.3
良い名前の条件

  • 説明的で意味・意図を表している
    • 省略のコツ
  • 一貫性がある
  • 英語で付けられている
    • オレオレ単語を作らない
    • スペルミスに注意する
    • 誤訳に注意する
    • 自信がないときは……
  • イディオムに従っている
  • コーディング標準に従っている
  • Column:名前の流行り廃り

3.4
変数名

  • 基本は説明的な名前を付ける
  • グローバル変数,クラス変数,インスタンス変数 ── 意味を正しく表現する
  • Column:変数の種類
  • ローカル変数 ── スコープの長さで使い分け
    • イディオムに従う/従わない
    • 変数名を短くしたほうが可読性が向上する場合もある
  • メソッドの引数名 ── わかりやすく簡潔に

3.5
メソッド名

  • Column:メソッドの種類

3.6
クラス名

  • クラス名のボキャブラリは経験とともに高まる

3.7
パッケージ/ネームスペース名

3.8
プロジェクト名

3.9
まとめ

  • Column:あるWebアプリケーションフレームワークでのクラス名の変遷

第4章 スコープ

4.1
スコープを意識していますか?

4.2
スコープって何?

4.3
スコープを小さくして覚えておくことを減らそう!

4.4
代表者の声

  • 良い仕事をしたい普通のプログラマー
  • 達人プログラマーを目指す中級プログラマー
  • 達人プログラマー

4.5
変数のスコープ

  • ローカル変数
    • 変数は使用する直前で宣言する
    • メソッドに抽出する
    • イテレータの一時変数のスコープをループ内に閉じ込める
    • 代入されない変数にはfinalを付ける
  • Column:JavaScriptの奇妙なスコープ
  • インスタンス変数
  • クラス変数

4.6
メソッドのスコープ

  • インスタンスメソッド
  • クラスメソッド
  • メソッドの引数の情報量

4.7
クラスのスコープ

  • インナークラス
  • 無名クラス

4.8
キャストを使用した可視性の制御

4.9
より大きな粒度のスコープ

4.10
まとめ

第5章 コードの分割

5.1
適切な長さにコードを分割する

5.2
なぜコードを分割するのか

  • 可読性が向上する
  • 保守性が向上する
  • 再利用性が向上する

5.3
代表者の声

  • 良い仕事をしたい普通のプログラマー
  • 達人プログラマーを目指す中級プログラマー
  • 達人プログラマー

5.4
2つの方向からの分割

  • トップダウン方式
  • ボトムアップ方式

5.5
お題 クライアントにXMLを返すWeb APIの処理を分割する

5.6
ステップ1 ベタなコードで書いてみる

5.7
ステップ2 共通処理をメソッドに抽出して分割する

  • 考察 どこまで共通化を行えばいいの?

5.8
ステップ3 処理単位で分割する

  • 考察 制御構造と処理の分け方
    • ループと,ループの中の処理
    • if文と,その中の処理
    • try〜catch〜finallyと,その中の処理

5.9
ステップ4 状態を持つ処理をクラスに抽出して分割する

  • 考察 インナークラスとして実装しているのはなぜ?
    • 実際に使用される箇所のすぐ近くにクラスがあるのでわかりやすい
    • もとのクラスとの依存関係をstaticで制御できる
    • 新しいファイルを増やさなくてよい
  • 考察 必要なデータをコンストラクタで渡しているのはなぜ?
  • 考察 インスタンス変数として保持したほうがよいものは何?
  • 考察 さらにリファクタリングしてみる

5.10
まとめ

第6章 コードの集約

6.1
コードの重複は悪

6.2
代表者の声

  • 良い仕事をしたい普通のプログラマー
  • 達人プログラマーを目指す中級プログラマー
  • 達人プログラマー

6.3
メソッドに抽出してまとめる

6.4
継承でまとめる

  • 継承でのまとめ方には注意
    • 親クラスが肥大する
    • 単一継承の制限

6.5
ユーティリティクラスにまとめる

  • Javaではstaticインポートと組み合わせると便利
  • ユーティリティクラスの名前付け

6.6
サービス層にまとめる

6.7
オブジェクトにまとめる

6.8
定数にまとめる

6.9
列挙型(enum)にまとめる

6.10
まとめ

  • Column:まとめすぎにご用心

第7章 データ構造

7.1
データ構造で勝負が決まる

7.2
代表者の声

  • 良い仕事をしたい普通のプログラマー
  • 達人プログラマーを目指す中級プログラマー
  • 達人プログラマー

7.3
データ構造とは?

  • 日付のデータ構造の例
    • 年月日をそれぞれの値で保持する場合
    • 年月日を配列で保持する場合
    • 年月日をオブジェクトのプロパティとして保持する場合
  • 複数のメールアドレスのデータ構造の例
    • すべてのメールアドレスを配列で保持する場合
    • メインとサブのメールアドレスを別のプロパティで保持する場合

7.4
データ構造の指針

  • 仕様や制約が明確
  • 処理するコードが書きやすい
  • パフォーマンスが劣化しにくい

7.5
お題 美容室の予約画面のHTMLを出力する

7.6
ステップ1 データベースのデータ構造をそのまま利用する

7.7
ステップ2 処理に最適なデータ構造を把握する

7.8
ステップ3 最適なデータ構造に変換して利用する

  • 考察 データの変換処理とHTMLの出力処理を分ける
  • 考察 効率的なデータ変換の方法
  • 考察 クラスを使うとどうなる?

7.9
まとめ

  • Column:データ構造はどこにでも存在している

第8章 コードのパフォーマンス

8.1
パフォーマンスを意識していますか?

8.2
代表者の声

  • 良い仕事をしたい普通のプログラマー
  • 達人プログラマーを目指す中級プログラマー
  • 達人プログラマー

8.3
パフォーマンスは計算量で決まる

  • アルゴリズムの選択で変わる
  • クラスの選択で変わる
  • ライブラリの使い方で変わる

8.4
パフォーマンスチューニングの手順

  • (1)まずは測定する
    • ログ出力による測定
    • プロファイラによる測定
  • (2)原因を特定する
  • (3)チューニングする
  • (4)チューニング結果を測定する

8.5
アルゴリズムの選択以外のパフォーマンスチューニング

  • SQLやテーブル設計を変更する
  • キャッシュする
  • インフラを強化する

8.6
パフォーマンスチューニングの指針

  • どのタイミングでチューニングするのがベストなの?
  • 適切な量のテストデータを用意しよう
  • 常にパフォーマンスを意識しよう

8.7
まとめ

第9章 ユニットテスト

9.1
テストはお好きですか?

9.2
ユニットテストって何?

9.3
代表者の声

  • 良い仕事をしたい普通のプログラマー
  • 達人プログラマーを目指す中級プログラマー
  • 達人プログラマー

9.4
ユニットテストの効能

  • 網羅的なテストを自動化できる
  • 回帰テストによりコードが壊れていないことを保証できる
  • 設計の改善につながる

9.5
お題 Webアプリケーションのセキュリティテスト

9.6
ステップ1 データベースにテストデータを登録する

9.7
ステップ2 画面の実装

9.8
ステップ3 画面のユニットテスト(正常系)

9.9
ステップ4 画面のユニットテスト(異常系)

9.10
ユニットテストの指針

  • テストのポリシーを決めておこう
  • テストしやすい部分はどこ?
  • テストしにくい部分はどうする?
    • 初期データのセットアップ
    • モックオブジェクト

9.11
まとめ

  • Column:言語別のテスティングフレームワーク

第10章 抽象化

10.1
抽象化がプログラミングのパワーを最大化する

10.2
配列/リストって何?

10.3
配列/リストを利用した抽象化とは?

10.4
代表者の声

  • 良い仕事をしたい普通のプログラマー
  • 達人プログラマーを目指す中級プログラマー
  • 達人プログラマー

10.5
お題 画像ファイルの一覧を表示するWebアプリケーション

10.6
ステップ1 ベタなコードで書いてみる

10.7
ステップ2 可読性を高めるためのメソッド抽出

10.8
ステップ3 関連するデータのデータ構造を整理

10.9
ステップ4 配列/リスト化して抽象化

10.10
抽象化の指針

  • どのタイミングで抽象化するのがベストなの?
  • コードの重複をまとめるな!?
  • これって単なるループなんじゃない?

10.11
まとめ

第11章 メタプログラミング

11.1
プログラミングをプログラミングする

11.2
代表者の声

  • 良い仕事をしたい普通のプログラマー
  • 達人プログラマーを目指す中級プログラマー
  • 達人プログラマー

11.3
メタプログラミングって何?

  • コードの自動生成
  • DSL
    • 外部DSL
    • 内部DSL
  • Column:流れるようなインタフェースとstaticインポートによる内部DSL

11.4
お題 Javaコードを使った内部DSL

  • 固定長電文解析処理をDSLで処理する

11.5
ステップ1 ベタなコードで書いてみる

  • 考察 インナークラスを使っているのはなぜ?

11.6
ステップ2 メタデータを内部DSLに移動する

  • 考察 メタデータって何?

11.7
ステップ3 変換ルールに対応する

  • ストラテジパターン
  • 考察 複数の変換パターンを適用したい場合は?
  • 考察 変換ルールに引数を追加したい場合は?
  • 考察 DSLの構文を改善するには?
    • コンストラクタをなくす
    • もっとDSLっぽくする
  • 考察 DSLのテストはどうする?
  • 考察 DSLのデバッグはどうする?
  • 考察 これはフレームワーク? それともDSL?

11.8
まとめ

第12章 フレームワークを作ろう

12.1
フレームワークの動作原理を知る

12.2
代表者の声

  • 良い仕事をしたい普通のプログラマー
  • 達人プログラマーを目指す中級プログラマー
  • 達人プログラマー

12.3
お題 Webアプリケーションフレームワークを作ろう

12.4
ステップ1 素のサーブレットで書いてみる

12.5
ステップ2 フロントコントローラとアクションクラスの導入

12.6
ステップ3 ルーティング情報の外部ファイル化

  • Column:JavaのリフレクションAPI

12.7
ステップ4 よく使う処理を簡単に実行できるように共通化する

  • 考察 これからさらにフレームワークを拡張するには?
    • クエリパラメータの自動バインディング
    • クエリパラメータの入力検証
    • その他

12.8
ステップ5 フレームワークをパッケージ化する

  • (1)フレームワークのコードとアプリケーションコードを分ける
  • (2)パッケージ化する
  • (3)バージョンを付けて管理する

12.9
まとめ

付録A コードリーディングの方法

A.1
コードには動的な読み方と静的な読み方がある

A.2
お題 Apache Commons IOのコードを読む

A.3
ステップ1 対象のコードをダウンロード(チェックアウト)する

A.4
ステップ2 静的な方法でコードを読む

  • 検索コマンドを使う
  • 統合開発環境を使う

A.5
ステップ3 動的な方法でコードを読む

  • デバッガで実行する
  • ユニットテストを実行する
  • コードを修正して実行する
  • 部分的なコードをコピーして使ってみる

A.6
まとめ

付録B 解説付き参考文献

    • あとがき
    • 索引
    • 著者プロフィール

サポート

ダウンロード

本書のサンプルコードは以下のページで公開しています。

(2022年7月1日更新)

正誤表

本書の以下の部分に誤りがありました。ここに訂正するとともに,ご迷惑をおかけしたことを深くお詫び申し上げます。

(2022年7月1日最終更新)

以下,第1刷までの内容に対する訂正となります。

P.34 表3.2の4行目

genarate
generate

P.52 下から2行目

Arrays.asList(new String[] {"blue", "red", "green"});
new ArrayList<>(Arrays.asList("blue", "red", "green"));

P.53 2つ目のコード

public class Colors {
    // Collections#unmodifiableListメソッドを使って
    // 変更不可能なListを作成
    public static final List<String> BASIC_COLORS =
        Collections.unmodifiableList(
          Arrays.asList(new String[] {"blue", "red", "green"}));
}
public class Colors {
    // List#ofメソッドを使って
    // 変更不可能なListを作成
    public static final List<String> BASIC_COLORS =
        List.of("blue", "red", "green"));
}

P.82 6行目

private Document buildDocument() throws Exception {
public Document buildDocument() throws Exception {

P.109 コードの3行目

interface Person = {
interface Person {

P.114 14行目

const time = timeReservation[i].time;
const time = timeReservation.time;

P.117 リスト7.5

...
// ❶ユニークな日付の配列を作成
const buildDates = (reservations) => {
  const dates = [];
  for (r of reservations) {
    if (!dates.includes(r.date)) {
      dates.push(r.date);
    }
  }
  return dates;
};

// ❷時間ごとの予約データの配列を作成
const buildTimeSlots = (reservations) => {
  const timeSlots = [];
  const times = [];
  for (timeReservation of reservations) {
    if (!times.includes(timeReservation.time) {
      // 対象時間のすべての日付の予約データの配列を作成
      const dateReservations = reservations.filter((r) => {
        return r.time == timeReservation.time;
      });
      const timeSlot = {
        time: r.time,
        dateReservations: dateReservations,
      };
      timeSlots.push(timeSlot);
      times.push(r.time);
    }
  }
  return timeSlots;
};
...
...
// ❶ユニークな日付の配列を作成
const buildDates = (reservations) => {
  const dates = [];
  for (const r of reservations) {
    if (!dates.includes(r.date)) {
      dates.push(r.date);
    }
  }
  return dates;
};

// ❷時間ごとの予約データの配列を作成
const buildTimeSlots = (reservations) => {
  const timeSlots = [];
  const times = [];
  for (const timeReservation of reservations) {
    if (!times.includes(timeReservation.time)) {
      // 対象時間のすべての日付の予約データの配列を作成
      const dateReservations = reservations.filter((r) => {
        return r.time == timeReservation.time;
      });
      const timeSlot = {
        time: timeReservation.time,
        dateReservations: dateReservations,
      };
      timeSlots.push(timeSlot);
      times.push(timeReservation.time);
    }
  }
  return timeSlots;
};
...

P.118 リスト7.6

...
// ❶データ構造を変換
const dates = buildDates(reservations);
const timeSlots = buildTimeSlots(reservations);

console.log('<table>');

// ❷1行目(日付の行を出力)
console.log('<tr>');
console.log('<th>日付</th>');
for (const date of dates) {
    console.log(`<th>${date}</th>`);
});

// ❸2行目以降(時間ごとの予約状況の行を出力)
for (const timeSlot of timeSlots) {
    console.log('<tr>');
    console.log(`<td>${time}</td>`);
    for (r of timeSlot.dateReservations) {
      console.log(`<td>${r.count}/${r.limit}</td>`);
    }
    console.log('</tr>');
});

console.log('</table>');
...
// ❶データ構造を変換
const dates = buildDates(reservations);
const timeSlots = buildTimeSlots(reservations);

console.log('<table>');

// ❷1行目(日付の行を出力)
console.log('<tr>');
console.log('<th>日付</th>');
for (const date of dates) {
    console.log(`<th>${date}</th>`);
}

// ❸2行目以降(時間ごとの予約状況の行を出力)
for (const timeSlot of timeSlots) {
    console.log('<tr>');
    console.log(`<td>${time}</td>`);
    for (const r of timeSlot.dateReservations) {
      console.log(`<td>${r.count}/${r.limit}</td>`);
    }
    console.log('</tr>');
}

console.log('</table>');

P.187 8行目
P.188 コードの1行目
P.190 下から13行目
P.194 リスト11.9の6行目

Map<String, Object> record = new HashMap<String, Object>();
Map<String, Object> record = new HashMap<>();

P.192 リスト11.6の3行目

return StringUtils.trim(value.toString());
return value.toString().trim();

(以下2021年4月28日更新分)

P.50 リスト4.aの2~4行目(2022年7月1日修正)

int total = 0;
for (int i = 1; i < 10; i++) {
    int n = i * i;
    total += n;
}
alert(total);
var total = 0;
for (var i = 1; i < 10; i++) {
    var n = i * i;
    total += n;
}
alert(total);

P.50 リスト4.bの2行目

int total, i, n;
var total, i, n;

商品一覧