LoginSignup
148
140

More than 5 years have passed since last update.

CleanCode読書メモ

Last updated at Posted at 2014-08-03

Clean Code アジャイルソフトウェア達人の技 の読書メモ。

本書は、Clean Code――すなわち、綺麗で読みやすいコードを書くための様々なテクニックや考え方を紹介している。

本書は、大きく以下の3つのパートに分かれている。

  1. Clean Code を書くための原則・パターンの紹介・解説(第2~13章)
  2. 実際のコードをリファクタリングしながら Clean Code にしていく様子を説明したケーススタディ(第14~16章)
  3. コードのにおい、経験則をまとめたカタログ(第17章)

ここでは、パート1と3の内容をメモする。
パート2については、どんなコードが対象になっているかの軽い紹介だけ。

基本的に「どうすべきか」の部分だけをまとめる。
「何故そうするのか?」の部分は実際の本を参照してください。

第2章 意味のある名前

意図が明確な名前にする (P.43)

  • 変数の名前は、何の値が入っているのかはっきりと分かるものにする。

偽情報を避ける (P.45)

  • List じゃないのに xxxxList のような名前をつけてはいけない。
  • 複数の意味を持ち、読み手に誤解を招くような単語や略語は使ってはいけない。
  • 長い名前の一部だけが異なるような名前を付けてはいけない(例:XYZControllerForEfficientHandlingOfStringsXYZControllerForEffecientStorageOfStrings

意味のある対比を行う (P.47)

  • 対比される対象がある場合に、違いがはっきりと分かる名前を付ける。
    • 同一スコープ内の変数に name1name2 のような名前を付けてはいけない。
    • 2つのクラスに ProductInfoProducetData のような名前を付けてはいけない。
    • 2つのメソッドに、 getAccount()getAccountInfo() のような名前を付けてはいけない。

発音可能な名前を使用する (P.48)

  • 人間が自然と発音できる名前を使用する。
  • この本では、実在した例として genymdhms() という名前を挙げている。
  • generate year month day hour minute second の略で、読みは「ジェン ワイ エム ディー エイチ エム エス」。

検索可能な名前を用いる (P.49)

  • grep などで目的の箇所を検索しやすい名前にする。
  • 一文字の変数などは検索しづらい。
  • マジックナンバーは検索しづらい。
    • 数字の 5 で grep する場合と、 WORK_DAYS_PAR_WEEK で grep する場合だと、後者の方が目的の箇所を検索しやすい。

エンコーディングを避ける

  • エンコーディングとは、型やスコープの情報を変数やメソッドの名前に含める手法のこと(_privateMemberi_value のようなの)。

ハンガリアン記法 (P.50)

  • Java などの明確な型が宣言できる言語では、ハンガリアン記法は使用しない。

メンバープレフィックス (P.51)

  • メンバー変数であることを表すメンバープレフィックスは使用しない。

インターフェースと実装 (P.52)

  • インターフェースの名前の先頭に I を付けない。

メンタルマッピングを避ける (P.52)

  • 別の人がコードを読んだ時に、「aaccount のことだな」のような脳内変換(メンタルマッピング)が必要になる名前を付けてはいけない。

クラス名 (P.53)

  • クラス名は名詞で名付ける。
  • Manager, Processor, Data, Info のような単語の使用は避ける。
  • クラス名を動詞にするのは避ける。

メソッド名 (P.53)

  • メソッド名は動詞で名付ける。
  • コンストラクタがオーバーロードされている場合、 static なファクトリメソッドを検討する。

気取らない (P.54)

  • 俗語や、一部の人にしか通じないようなジョークを交えた名前を付けてはいけない。

1つのコンセプトは1つの単語 (P.54)

  • 例えば、何らかの値を取得するメソッドの名前が、クラスによって get だったり retrieve だったり fetch だったりするのを避け、同じ名前を付けるようにする。

ごろ合わせをしない (P.55)

  • 1つの単語に2つ以上の意味を持たせてはいけない。
  • 例えば、以下のような異なる意味を持つ add メソッドを作ってはいけない。
    • ある add メソッドは、第一引数のオブジェクトに第二引数の値を追加する。
    • ある add メソッドは、そのオブジェクトに引数で指定した値を追加する。
  • 既に存在するメソッドの大部分が前者だった場合、後者は append と名付けた方が良い。

解決領域の用語の使用 (P.55)

  • 解決領域の名前に、コンピュータサイエンスの用語・アルゴリズムの名前・パターンの名前・数学用語を用いるのは全く問題ない。
  • 解決領域を全て業務用語で名付けるのは避ける。

問題領域の用語の使用 (P.56)

  • 問題領域の名前は、業務用語から名付ける。

意味のある文脈を加える (P.56)

  • それ単独だと文脈がわかりづらい変数がある場合、適切なクラスや関数、名前空間に属させる。
  • 例えば、 name という変数が使用されているとき、それだけでは「何の名前か?」がわかりづらい。
  • name が管理者の名前だとした場合、 Administrator クラスの中に移したり、管理者を操作する小さな関数の中に移動することで「管理者の名前」という文脈が分かりやすくなる。

根拠の無い文脈を与えない (P.58)

  • 必要以上に、名前に文脈を付けてはいけない。
  • 例えば、あるシステム ABC に含まれるクラスだからといって、その全てのクラス名の先頭に ABC を付けるようなことは、してはいけない。
  • 長い名前より、短く意味が明確な名前の方が優れている。

第3章 関数

小さいこと! (P.64)

  • 関数の第一規則は、小さくせよ。第二の規則は、さらに小さくせよ
  • 関数の長さが20行に達することなど、ほとんどないようにすべき。
  • 理想は1関数 2~5 行程度。

ブロックとインデント (P.65)

  • 理想の行数を実現しようとした場合、必然的に if 文などのブロックは入れ子にしてはいけないことが示唆される。
  • 必然的に、ブロックの中は分割した別の関数の呼び出しになる。

1つのことを行う (P.65)

  • 関数では1つのことを行うようにせよ。その1つのことをきちんと行い、それ以外のことを行ってはならない。
  • 「1つのこと」とは、「同じ抽象レベルで行う一連の処理」のこと。
  • 関数を書く目的は、ある抽象レベルの1つの処理を、次のより詳細な抽象レベルのいくつかのステップに分解することにある。

関数内のセクション (P.67)

  • ある関数の中身を、いくつかのカタマリ(セクション)に分類できる場合、その関数は1つ以上のことをしている。
  • 1つのことをしている関数は、セクションに分けることが難しくなる。

1つの関数に1つの抽象レベル (P.67)

  • 1つの関数の中にある各ステップは、すべて同じ抽象レベルの処理にする。

コード通読:逓減(テイゲン)規則 (P.67)

  • 関数は、 TO 節の並びとして読めるのが良い。
  • TO <関数名> , <関数内の処理>
    • <関数名> するためには、 <関数内の処理> を行う。

switch 文 (P.68)

  • switch 文はポリモーフィズムで置き換える。
  • ファクトリメソッド内のように継承の裏に隠された場所での switch 文の使用は許容できる。

内容をよく表す名前を使う (P.70)

  • 関数の中で何を行っているかがよく分かる名前を付ける。
  • 関数が小さいほど、名前を付けるのが簡単になる。
  • 内容をよく表す長い名前は、不可解な短い名前よりも優れている。
  • 内容をよく表す長い名前のほうが、内容を説明した長いコメントよりも優れている。

関数の引数 (P.71)

  • 関数の引数の理想は、0(無し)。
  • 3つ以上の引数は避ける。4つ以上はよほどの理由がない限り避ける。

共通モナディック形式 (P.72)

  • 1引数の関数には、以下の一般的な形式がある。
    • 受け取った引数について判定し、 boolean を返す形式。
    • 受け取った引数を変換し、別の値を返す形式。
    • イベントが発生したときに呼び出される関数の形式。イベントに関する情報が引数で渡され、戻り値は無い(void)。
  • これらの形式とは異なる動きをする関数は、作ってはいけない。

フラグ引数 (P.73)

  • フラグ引数は使ってはいけない。
  • true/false の場合で、それぞれ関数を定義すべき。

引数2つの関数 (P.73)

  • 2つの引数が、2つセットであることが自然である場合(デカルト座標など)は、引数2つの関数も問題ない。
  • 引数が2つある関数が存在した場合、1つにできないか検討する。

引数3つの関数 (P.74)

  • 引数3つの関数は、引数2つよりも理解が難しい。

引数オブジェクト (P.74)

  • 引数が多い場合、同じ概念の引数はそれらをまとめたクラスにラップする(引数オブジェクト)。

引数リスト (P.74)

  • 可変長引数が全て同じ用途で使われている場合、それは1つの List の引数とみなすことができる。
  • 可変長引数のそれぞれを異なる用途で使う場合、それは2つ以上の引数が存在するのと同じになり、避けるべき。

動詞とキーワード (P.75)

  • 関数名には、引数を説明するキーワードを含めたほうが良い。
  • write(name) よりも、 writeField(name) の方が良い。

副作用を避ける (P.76)

  • 副作用とは偽りです。
  • 関数が1つのことを行うことを保証しつつ、隠れて別のことを行うのです。

出力引数 (P.76)

  • 出力引数は避けるべき。
  • 関数が何らかの状態を変更しなければならないのであれば、自分自身の状態を変更すべき。

コマンド・照会の分離原則 (P.77)

  • 1つの関数は、以下のいずれかのみを行うべき。
    • 何らかの処理を行う(コマンド)
    • 何らかの応答を返す(照会)
  • 1つの関数が、コマンドと照会の両方を行うべきではない。

戻りコードよりも例外を好む (P.78)

  • エラーがある場合、エラーコードを返すのではなく例外をスローする方が良い。

try/catch ブロックの分離 (P.79)

  • try ブロック、 catch ブロックをそれぞれ単一の関数に抽出する。
public void method() {
    try {
        execute();
    } catch (Exception e) {
        handleError(e);
    }
}

private void execute() throws Exception {
    //...
}

private void handleError(Exception e) {
    //...
}

エラー処理も1つの処理 (P.80)

  • エラー処理も1つの関数として抽出する。
  • エラー処理の関数は、エラー処理以外を行ってはいけない。

Error.java依存性磁石 (P.80)

  • エラーコードを返す関数を作っている場合、エラーコードをまとめた enum が存在することになる。
  • この enum はあらゆるクラスから依存される磁石のような存在になる。

DRY(Don't Repeat Yourself)原則 (P.81)

  • 重複は排除すべし。

構造化プログラミング (P.81)

  • 関数が十分に小さければ、複数の return や break、 continue の存在は問題にならない。
  • 関数が十分に小さければ、 goto 文は不要になる。

なぜ関数をこのように書くのでしょう? (P.82)

  • プログラムを書くことは、文章を書くことと同じ。
  • 最初の草稿はぎこちないが、読者が読みたいと思えるような文章になるまで推敲を重ねる。
  • 最初から完璧なものが書けるわけがない。そんなことは誰にもできない。

第4章 コメント

コメントで、ダメなコードを取り繕うことはできない (P.89)

  • コメントを書くことに時間をかけるより、その時間をコードを綺麗にすることに費やすべき。

自分自身のコードの中で説明する (P.89)

  • コメントで説明するのではなく、変数名や関数名から読み取ることができるように名前を付ける。

よいコメント (P.89)

まっとうなコメント (P.90)

  • ライセンスや著作権のコメントは必要なコメント。
  • ただし、本文は別途用意し、そこへの参照のみ記載するなどして、可能な限り短くすると良い。

情報を与えるコメント (P.90)

  • コードには記載されていない追加の情報を与えるコメントは有益となる。
  • しかし、その追加情報をコードで表現すれば、コメントは結局不要になる。

意図の説明 (P.91)

  • 「何故そうしたのか?」のような意図の説明はコードで表現することができないので、補足としてコメントを残すのは有益。

明確化 (P.92)

  • 曖昧な情報や、解読が困難な実装に対して、意味を明確化するためのコメントは有益。
  • ただし、これも曖昧ではない実装、可読性の高い実装にコードを修正すれば、コメントは不要になる。

結果に対する警告 (P.93)

  • 例えば、あるメソッドの処理時間が非常に遅い場合、それを頻繁に使用しないよう警告のためにコメントを記載するのは有益。
  • ただし、可能な限り使用するフレームワークやライブラリがサポートしている方法で警告を明示するほうが良い(JUnit の @Ignore など)

TODO コメント (P.94)

  • 後で行うことを // TODO <内容> というコメントを残すことで、あとで検索がしやすくなり、有益になることがある。
  • ただし、 TODO コメントは常に潰していくようにしなければならない。

強調 (P.94)

  • 一見すると不可解に思える実装について、その理由を説明するためのコメントは有益になる。

公開 API における Javadoc (P.95)

  • 公開 API のために Javadoc を書くのは良いこと。
  • ただし、 Javadoc 自体が誤解を招くような記述になってはいけない。

よくないコメント

ぶつぶついう (P.95)

  • たとえその時は不満が溜まっていたとしても、他人が見て意味が分からないコメントを残すようなことをしてはいけない。

冗長なコメント (P.96)

  • コードで書かれていることを、そのまま説明しているだけのコメントは不要。

誤解を招くコメント (P.99)

  • 読み手に誤解を招くようなコメントを書いてはいけない。

命令コメント (P.99)

  • 全ての関数や変数に Javadoc やコメントを書くことを強制するのは馬鹿げている。

日誌コメント (P.100)

  • モジュールの変更内容を履歴としてコメントに残すようなことは、してはいけない。
  • 変更管理は、専用のツール(CVS, SVN, Git)に任せるべき。

ノイズコメント (P.100)

  • まったく新しい情報を与えないコメントはノイズでしかない。

恐るべきノイズ (P.103)

  • 新しい情報を与えないのであれば、 Javadoc もノイズになり得る。
  • 新しい情報を与えないだけでなく、コピペミスなどで嘘情報を与えるコメントが生まれることがある。

関数や変数が使用できるのであれば、コメントを使用しないこと (P.103)

  • コメントでダラダラと処理の内容を説明するくらいなら、それをコードで表すべし。

道標 (P.104)

  • 以下のようなバナーは削除すべきゴミ。
// 事前処理 ///////////////////////////////////////////////////////

// メイン処理 /////////////////////////////////////////////////////

// 後処理 /////////////////////////////////////////////////////////

閉じカッココメント (P.104)

  • 以下のような閉じカッココメントを書くくらいなら、関数を小さくすることを試みる。
if (a == 0) {
    // 長大な if ブロック
    // ...
    // ...
} // a == 0

属性と署名 (P.105)

  • 誰が修正を加えたか、のようなコメントは変更管理システムに任せるべき。

コメントアウトされたコード (P.105)

  • 使わなくなったコードは、コメントアウトで消すのではなく、完全に削除すべし。
  • 変更管理システムを使っていれば、削除したところでいつでも元に戻すことができる。

HTML コメント (P.106)

  • コメントで HTML の記述が大量に現れるのは避けるべき。
  • 特に &lt; のようにエスケープ表現が連発すると、コメント自体の可読性が著しく低下する。

非局所的な情報 (P.107)

  • コメントは、そのコメントが指している場所のすぐそばに記述しなければならない。
  • コメントが書かれているところとは全く異なる箇所の説明を載せてはいけない。

多すぎる情報 (P.107)

  • 過度な情報量のコメントは不要。

不明確なつながり (P.108)

  • 実装のどの部分を説明しているのか分からないようなコメントは書くべきではない。
  • コメント自身が、さらに説明を要するのは情けないことです。

関数ヘッダ (P.108)

  • 関数が十分に小さければ、コメントは不要になる。

非公開コードの Javadoc (P.109)

  • 外部に公開されていない Javadoc は不要。

第5章 書式化

縦方向の書式化 (P.116)

  • 1ソースファイルにつき、長くても 200 ~ 500 行を限界にする。

新聞にたとえる (P.117)

  • 新聞記事は、一番上に見出しがあり、下に進むに連れて詳細な内容になる。
  • これと同じように、ソースコードも抽象度の高い関数から始まり、下に進むに連れて抽象度の低い関数が並ぶようにする。

垂直概念分離性 (P.118)

  • 各メソッドの間やインポート文とクラス定義の間などには、適度に空行を入れることで見やすくできる。

垂直密度 (P.119)

  • 強く関連する処理や変数は、間に空行やコメントを挟まないようにする
  • この結果、意味的なつながりを表現でき、可読性が上がる。

垂直距離 (P.120)

  • 関連の強い記述は、縦方向に近い場所に配置する。

変数宣言 (P.121)

  • 変数は、その変数が使用される直前で宣言する。

インスタンス変数 (P.122)

  • インスタンス変数は、クラス定義の一番上に並べる。

依存関数 (P.123)

  • 関数 A が、関数 B を呼び出している場合、 A -> B の順番で縦に続けて並べるようにする。

概念の密接な関係 (P.125)

  • 概念が同じようなものは、縦方向に近い場所に集める。

垂直方向の並び順 (P.126)

  • 関数は、呼び出す側が上に、呼び出される側が下に来るように並べる。

横方向の書式化 (P.126)

  • 横方向には、 80 ~ 120 文字程度に抑える。

水平分離性と密度 (P.127)

  • 空白スペースを利用することで、関連の強いものと弱いものを明確に分離する。
  • 例えば、以下の例の場合、掛け算の方が足し算よりも優先度が高いことを、空白スペースの有り無しで表現している。
int n = 2*10 + 13*2;

水平方向の位置合わせ (P.128)

  • 以下のような水平方向の位置合わせは行わない
int    id         = 12;
String name       = "Hoge";
Date   createDate = new Date();
  • 水平方向の位置合わせをするくらいなら、変数の数を減らすことを検討する。

インデント (P.130)

  • インデントを適切に入れることで、人間が読みやすいコードにできる。

チームの規則 (P.132)

  • チームで開発をする場合は、書式規約を設けるべき。

第6章 オブジェクトとデータ構造

データ抽象化 (P.137)

  • 軽い気持ちでゲッタとセッタを用意するのは最悪。
  • クラスとは、実装の詳細を隠し、抽象化された操作を利用者に提供するために存在する。

データ/オブジェクトの非対称性 (P.139)

  • データ構造は、データのみを公開し、意味のある操作は提供しない。
  • オブジェクトは、データ構造を隠蔽し、抽象化された操作を提供する。

  • 手続き型は、既存のデータ構造を変更することなく、新しい操作関数を追加することができる。
  • オブジェクト指向は、既存の操作関数を変更することなく、新しいデータ構造(クラス)を追加することができる。

  • 手続き型は、新しいデータ構造を追加すると、既存の操作関数を全て変更しなければならない。
  • オブジェクト指向は、新しい操作関数を追加すると、既存のデータ構造(クラス)を全て変更しなければならない。

※この本には当然書かれていないが、Java8 では後者の問題に対応するために、インターフェースにデフォルトメソッドが定義できるようになっている。


  • オブジェクト指向で難しいことは手続き型では容易であり、手続き型で難しいことはオブジェクト指向では容易。
  • 熟練したプログラマなら、オブジェクト指向が常に優れているという考えが神話であることを理解している。
  • 単純なデータ構造とそれらを操作する手続きのほうが必要とされる場合もある。

デルメルの法則 (P.142)

  • オブジェクトはデータを隠蔽し、操作を公開する。
  • デルメルの法則では、クラス C のメソッド f は、以下のオブジェクトのメソッドのみを呼び出すべきであると定義している。
    • C そのもの
    • f で生成されたオブジェクト
    • f の引数で渡されたオブジェクト
    • C のインスタンス変数に保存されたオブジェクト

電車の衝突 (P.143)

  • 以下のようなコードは、電車の衝突と呼ばれる。
電車の衝突
String outputDir = ctxt.getOptions().getScratchDir().getAbsolutePath();
  • 一見するとデルメルの法則に反しているように見えるが、参照している全ての値が「データ構造」なら問題はない。
  • データ構造なら、ゲッタを設けるのではなく、インスタンス変数を公開し以下のようにすれば混乱は避けられる。
データ構造なら、以下のように書けば問題はない
String outputDir = ctxt.options.scratchDir.absolutePath;

混血児 (P.144)

  • 抽象的なメソッドを持ちながら内部構造を公開する、オブジェクトとデータ構造を混ぜあわせたようなクラスを「混血児」と呼ぶ。
  • 混血児は作ってはいけない。

データ転送オブジェクト (P.145)

  • 典型的なデータ構造は、いわゆる DTO と呼ばれるクラス。

アクティブレコード (P.146)

  • アクティブレコードとは、 DTO をデータベースのレコードの写像として扱い、 savefind のような典型的な操作メソッドを持たせる手法。
  • アクティブレコードにビジネスロジックを持たせてはいけない。

第7章 エラー処理

リターンコードではなく、例外を使用する (P.150)

  • エラーが有った場合、例外を送出するようにする。

非チェック例外を使用する (P.153)

  • 一般的なアプリケーション開発では、チェック例外ではなく非チェック例外を使うのが良い。

例外で状況を伝える (P.154)

  • 例外には、エラーの原因を説明するメッセージを設定する。

呼び出し元が必要とする例外クラスを定義する (P.154)

  • あるメソッドの呼び出し元で、複数の例外をキャッチし同じエラー処理をしている場合、それらの例外をラップした共通の例外を定義する。

正常ケースのフローを定義する (P.156)

  • 例外をキャッチした後も処理は継続するような場合は、例外を送出するのではなくマーチン・ファウラーのスペシャルケースパターンの利用を検討する。

null を返さない (P.158)

  • 関数は null を返す代わりに Null オブジェクトを返すようにする。

null を渡さない (P.159)

  • 関数に null を渡すことは、原則禁止にすべき。

第8章 境界

サードパーティーコードを使用する (P.163)

  • ライブラリを使用する場合、作成しているアプリのために API を限定したラッパークラスを設けるのが良い。

境界の調査と学習 (P.166)

  • サードパティーのライブラリを使う場合、アプリから利用する方法でライブラリのテストコードを書くのが良い。
  • このテストコードを、学習テストと呼ぶ。

学習テストは、タダ以上のものである (P.169)

  • 学習テストは、以下のメリットがある。
    • ライブラリの使い方を学習できる。
    • ライブラリが期待通りに動くことを検証できる。
    • ライブラリがアップデートされたときに、互換性があるかどうかを即座にチェックできる。

まだ存在しないコードを使用する (P.169)

  • 連携対象の実装がまだできていない場合は、以下の手順で実装する。
    • こちら側の視点で、期待するインターフェースを定義する。
    • 期待するインターフェースを元に実装を進める。
    • 連携対象が用意できた後は、期待するインターフェースと実際の実装の差異を吸収するアダプタークラスを作成し、連携対象と結合する。

きれいな境界 (P.171)

  • サードパーティーの詳細に関する知識を持つことは避けるべき。
  • 自分たち用に、制御可能な境界を設けるのが良い。

第9章 単体テスト

テストをきれいに保つ (P.175)

  • テストコードも、製品コードと同じか、それ以上にきれいに保たなければならない。

テストは、 xxx 性を可能とする (P.176)

  • テストは、以下の xxx 性を提供する。
    • 柔軟性
    • 保守容易性
    • 再利用性
  • 汚いテストは、存在しないものと同じ。

クリーンテスト (P.177)

  • 洗練されたテストには3つの要素がある。それは、読みやすさ、読みやすさ、そして読みやすさの3つです。
  • テストは、「構築」「操作」「検証」の3つに明確に分離(関数化)することで読みやすくなる。

ドメイン特化テスト言語 (P.180)

  • テストを読みやすくするために、テストのためだけに用意される関数やユーティリティをテスト言語と呼ぶ。
  • テスト言語は、度重なるリファクタリングによって導き出される。

二重規範 (P.180)

  • 製品コードでは決して書いてはいけないような手法でも、それがテストでしか使用されないコードであれば、読みやすさを優先して利用しても良い。
  • 例えば、 Java で文字列の連結は実行速度の観点から StringBuilder を使うべきとされている。しかし、そのコードがテストでしか呼ばれないのであれば、文字列リテラルで連結したほうが可読性が上がり利益が大きい。

1つのテストに1つのアサート (P.183)

  • 1つのテストメソッドにアサート文は1つという指針は、やや厳しすぎる。
  • 2つ以上のアサートを含めても構わない。しかし、アサートはなるべく少なくする。

1つのテストでは1つの概念を扱う (P.184)

  • 1つのテストの中で、複数の概念を扱ってはいけない。

F.I.R.S.T (P.186)

  • クリーンテストは、以下の条件を満たしている必要がある。
    • Fast : テストは高速に実行できる必要がある。
    • Independent : テストは互いに独立している必要があり、依存関係を持ってはいけない。
    • Repeatable : テストは、どんな環境でも実行できるようにしておかなければならない。
    • Self-Validating : テストは、結果を成功か失敗のいずれかで出力しなければならない。
    • Timely : テストは必要なときにすぐに書けるようにしておかなければならない(製品コードを書く前に、単体テストが書けるようにしておかなければならない)。

第10章 クラス

クラスの構成 (P.189)

  • Java のクラスの中身は、以下の順序で記述する。
    1. public な static 定数
    2. private な static 変数
    3. private な instance 変数
    4. public な関数
    5. public な関数から利用される、 private なユーティリティ関数

カプセル化 (P.190)

  • テストを優先し、テストから参照できるように可視性を緩めることもある。

クラスは小さくしなければならない! (P.190)

  • クラスの規則の筆頭は、小さくすること。第二は、さらに小さくすること。
  • クラスの大きさは、責務の数で測る。
  • クラスの責務を説明したときに、「もし」「そして」「あるいは」「しかし」と行った単語が出てきたら、責務が大きすぎるサイン。

単一責任の原則 (P.193)

  • クラスは、ただ1つの責務をもつようにしなければならない。
  • クラスが1つの責務を持つということは、そのクラスを変更する原因は1つに限られる、ということ。
  • システムは、多数の小さなクラスで構成させる。

凝集性 (P.197)

  • クラスは、凝集性を高めなければならない。
  • クラスが持つインスタンス変数を、クラスの全てのメソッドで使用している場合、凝集性が高いといえる。

凝集性に気を配ると、大量の小さなクラスが生まれる (P.195)

  • 大きな関数を小さな関数に分割することが、クラスをより小さなクラスへ分割することへとつながる。

変更のために最適化する (P.202)

  • 単一責任の原則に従ってクラスを分割することで、 OCP を満たせるようになる。
  • OCP (Open Closed Principle) とは、「開放/閉鎖原則」と言い、クラスは拡張に対して開かれており、変更に対して閉じていなければならない、という原則のこと。

変更から切り離す (P.205)

  • クラスは、依存性逆転の原則(Dependency Inversion Principle, DIP)に従うべき。
  • 依存性逆転の原則とは、クラスは抽象層にのみ依存すべきで、詳細な具象層には依存すべきではない、ということ。
  • 具体的には、具象クラスに依存するのではなく、インターフェースに依存するようにする。

第11章 システム

システムを使うことと、構築することとを分離する (P.210)

  • 全てのアプリケーションは、「オブジェクトの生成」と「依存関係の解決」という関心事を持つ。
  • システムを構築するうえで必要になるこれらの関心事は、システムの本来の処理からは分離すべき。

main の分離 (P.211)

  • 前述の関心事の分離を実現する最も簡単な手段は、 main 関数を用意して、そこで全ての依存関係の解決を行うこと。
  • この場合、依存関係の構築方法について、アプリケーションは知識を一切持たない。

ファクトリ (P.212)

  • アプリケーションがオブジェクトの生成について責任を持つ必要がある場合は、Abstract Factory パターンを使う。

依存性注入 (P.213)

  • 依存性注入(Dependency Injection : DI)は、依存関係の管理をアプリケーションから分離するための強力な仕組み。
  • セッタメソッドやコンストラクタ引数を通じて、依存対象がインジェクション(注入)される。
  • Spring フレームワークが、 Java の DI コンテナとして有名。

スケールアップ (P.214)

  • コードレベルでは、テスト駆動開発・リファクタリング・クリーンコードを通じて、インクリメンタルに拡張を加えることができる。
  • システムレベルでアーキテクチャのインクリメンタルな拡張を実現するためには、関心事を適切に分離しておく必要がある。

横断的関心事 (P.217)

  • 複数のオブジェクトに跨って記述される共通の処理のことを、横断的関心事と呼ぶ。
  • アスペクト指向プログラミング(aspect-oriented programming : AOP)では、これら横断的関心事を分離し、モジュール化するアプローチが取られる。

Java プロキシ (P.218)

  • Java には標準で AOP を実現するための仕組み――Java プロキシが用意されている。
  • Java プロキシを使う場合、インターフェースを用意しなければならない、という制約がある。
  • ちょっとした AOP を実現するだけでも、かなりの量のコードを記述しなければならない。

Pure Java の AOP フレームワーク (P.220)

  • Spring は、 AOP の設定を XML として分離することで、アプリケーションのコードがドメインに集中できるようになっている。
  • XML についても、設定より規約(convention over configuration)とアノテーションを用いることで、最小限の記述で済むようになっている。
  • EJB3 は、 Spring の手法を取り入れ、完全な POJO でコードを書けるようになっている。

AspectJ アスペクト (P.223)

  • ほとんどの場合は Spring AOP や JBoss AOP で事足りるが、もし足りないという場合は、 AspectJ の強力な機能を利用する方法がある。
  • AspectJ はツールに慣れるのがやや大変。

システムアーキテクチャのテスト実行 (P.224)

  • ドメインロジックが POJO で書かれ、アーキテクチャから分離されていることで、アーキテクチャを単純なものから洗練されたものへと進化させていくことができるようになる。

意思決定を最適化する (P.225)

  • 決定は、それが手遅れとなる直前まで延期することが最善である。
  • 関心事をモジュール化し変化に強くなったシステムは、ジャストインタイムで決断を反映できる。

論証可能な価値を追加する際には、標準を賢く使用する (P.226)

  • 「標準」は、人類が蓄積してきた知識の再利用を容易にする。
  • しかし、「標準」の策定には時間がかかり、時には真のニーズから乖離してしまうことがあり、利用には注意が必要。

システムはドメイン特化言語を必要とする (P.227)

  • 優れたドメイン特化言語は、ドメインの概念と実装コードとの間にあるコミュニケーションギャップを埋めることができる。

※私見
「ドメインの概念と実装コードとの間にあるコミュニケーションギャップを埋める」という役割は、 DDD では「ユビキタス言語」と「ドメインモデル」が対応していると思う。

第12章 創発

創発的設計を通して、洗練する (P.229)

  • 優れた設計を生み出すための4つの規則がある。
    1. 全テストを実行する
    2. 重複がない
    3. プログラマの意図が表現されている
    4. クラスとメソッドを最小化する

単純な設計への規則1:全テストを実行する (P.230)

  • 設計によって、システムは意図通りに動かせなければならない。
  • システムが意図通りに動いているかどうかを検証するためには、システムがテスト可能でなければならない。
  • SRP (Single Responsibility Principle:単一責任の原則) に従っていればいるほど、テストは簡単になる。
  • 逆に、クラス同士の結びつきが強ければ強いほど、テストは困難になる。

単純な設計への規則2~4:リファクタリング (P.230)

  • テストがあれば、コードをインクリメンタルにリファクタリングしていくことができるようになる。
  • リファクタリングでは、様々な優れたソフトウェア設計の知識を総動員することができる。

重複の排除 (P.231)

  • 優れた設計のシステムにおける最大の敵は、重複。
  • たった数行の重複も、取り除かなければならない。
  • Template Method パターンは、重複を取り除くのにうってつけの技法。

表現に富む (P.234)

  • 変更を行うときに不具合を混入させないようにするためには、システムの動作を「理解可能」な状態にしておく必要がある。
  • 「理解可能」とするためには、コードに書き手の意図が明快に表現されている必要がある。
  • 以下のことを行えば、意図を明快に表現できる。
    • 関数とクラスを小さく保つ。
    • 標準の用語を使う(デザインパターン名など)。
    • 適切に記述された単体テストを書く。

クラスとメソッドを最小限に (P.235)

  • 重複の排除・SRP などは、行き過ぎてしまうと、小さなクラスやメソッドがあまりに多く生成されてしまう危険性がある。
  • 全てのクラスにインターフェースを設けるような実装ルールは、独善的でいたずらにインターフェース数を増大させてしまうため、設けるべきではない。
  • ただし、この規則は他の規則に比べると優先度は低い。
  • クラスやメソッドの数を減らすのは重要だが、それよりも、まずはテストを書き、重複を排除し、コードの意図を表現することの方が重要。

第13章 同時実行性

なぜ同時実行性が必要なのか? (P.238)

  • 同時実行性は、「何を実行するか」と「いつ実行するか」の分離に役立つ。
  • 同時実行は、システムの応答時間を改善するのに利用できる。

神話と誤解 (P.239)

  • 同時実行性がパフォーマンスを改善するのは、待ち時間が大量にあり、それを複数スレッドあるいはプロセッサで共有できる場合のみ。
  • 「何」を「いつ」から分離すると、通常はシステムの構造に多大な影響を与える。
  • EJB コンテナなどを使っている場合も、コンテナの中でどのように処理が行われているか把握しておく必要がある。

難問 (P.240)

  • 同時並行プログラムが難しくなる原因は、問題が起こるパターンが想定しづらい、という点にある。
  • 想定しづらい原因は、以下の2つに依る。
    • バイトコードレベルで見ると、ソースの実行経路が大量に存在する。
    • 大量の実行経路のうち、大部分はうまく動く。しかし、一部だけがうまく動かないことがある。

同時実行性防御原則 (P.241)

単一責務の原則 (P.241)

  • 同時並行処理は、それ単独で1つの責務と考えることができる。
  • 同時並行性に関係するコードは、他のコードから分離すること。

帰結:データのスコープを限定せよ (P.241)

  • 同時並行処理の中で共有されるデータの数は、できる限り少なくすること。

帰結:データのコピーを使用せよ (P.242)

  • データの共有をやめ、コピーを渡すようにする。そうすることで、同期化やロックなどのややこしい制御を避けることができる。

帰結:スレッドはできるかぎり独立させよ (P.242)

  • それぞれのスレッドが独立したスコープの変数のみを扱う場合、同期は不要になり実装が容易になる。

使用しているライブラリを知る (P.243)

スレッドセーフなコレクション (P.243)

  • java.util.concurrent.ConcurrentHashMap は、以下の性質を持つ。
    • HashMap よりも、ほぼ全ての状況で高速に動作する。
    • 同時並行的に読み書きすることができる(スレッドセーフ)。
  • Java5 以降の環境であれば、 ConcurrentHashMap の使用を検討する。
  • 他にも、 java.util.concurrent の下には同時並行処理の実装に有益なクラスがあるので、調査しておくべき。

実行モデルを見分ける (P.244)

  • 同時並行処理で処理を制御する方法には、大きく以下の3つがある。
    1. プロデューサ・コンシューマ
    2. リーダー・ライター
    3. 哲学者の食事
  • これらのアルゴリズムの基本をしっかり理解しておく必要がある。

プロデューサ・コンシューマ (P.244)

  1. プロデューサは、スレッドを生成してキューに追加する。
  2. プロデューサは、キューにスレッドが入ったことをコンシューマに通知する。
  3. コンシューマは、キューに入ったスレッドを取り出し、処理をする。
  4. コンシューマは、キューが空になったことをプロデューサに通知する。
  5. 1 に戻る。

リーダー・ライター (P.245)

  • 読み書きされる共有リソースが存在する場合、許容されるスループットの中で、リーダーとライターの優先度を決める。
  • リーダーを優先した場合、スループットは上がるが、ライターの処理が中断されやすくなる。
  • ライターを優先した場合、更新は速くなるが、スループットは落ちる。

哲学者の食事 (P.245)

  • 複数の共有リソースを、各スレッドが定められた順番でロックするようにする方法。
  • 注意深く設計しないと、デッドロック・ライブロックなどの問題が生じる。

同期化メソッド間の依存関係に注意 (P.246)

  • 以下のような条件を満たす実装をした場合、微妙なバグを誘発する危険性がある。
    • 同期化されたメソッドが複数あり、一方が他方の処理に依存している。
    • これらのメソッドを、続けて呼び出している。
  • どうしても、上記のような実装をしなければならない場合、以下のような対策を講じる必要がある。
    • 呼び出し側で、ロックの制御を行う(クライアントベースロック)。
    • 呼び出し元で、ロックの制御を行う(サーバーベースロック)。
    • 呼び出し側と呼び出し元の間に、ロック用の中間層を設ける(サーバー適合)。

同期化セクションを小さくする (P.246)

  • 同期化する領域(セクション)は、できる限り小さくしなければならない。

正確な終了処理コードを書くのは難しい (P.247)

  • 同時並行処理の終了処理を正確に記述するのは困難。
  • 同時並行処理の記述が必要とわかった場合は、早い段階で終了方法の考慮を初めておかなければならない。
  • これは、思った以上に時間を要する。

スレッド化されたコードのテスト (P.247)

  • スレッドで動くコードのテストは非常に困難。

怪しい失敗を、スレッド問題の容疑者として扱う (P.248)

  • たまにしか起きないバグがあった場合、それはスレッド処理に原因があると疑ってかかるべき。

最初にスレッド化されていないコードを完成させる (P.248)

  • 非マルチスレッドな処理は、テストも含め先に完成させておくこと。

スレッド化されたコードは差し替え可能とする (P.249)

  • マルチスレッドで動かすコードは、シングルスレッドでも動かせるように、差し替え可能な形で設計する。

スレッド化されたコードをチューニング可能にしておく (P.249)

  • システムのパフォーマンスを測る方法を用意しておく。
  • 実行時にスレッドの数を変更できるように設計しておく。

プロセッサの数よりスレッドの数を多くする (P.249)

  • プロセッサの数よりスレッドの数を多くすることで、同時並行処理の問題をあぶり出しやすくなる。

異なるプラットフォームで実行する (P.249)

  • マルチスレッドのコードは、環境が変わると異なる挙動をする。
  • 早い次期に、全ての対象プラットフォームでスレッド化されたコードを動かすようにする。

コードに対していろいろなことを試し、強制的にエラーを発生させる (P.250)

  • コードを変更することで、強制的にエラーを発生させる手法がある。
  • 手作業で行うものと、自動で行うものがある。

手作業によるもの (P.250)

  • wait()sleep()yield()priority() といったメソッドを、手作業でコード上に追加してエラーを発生させ、問題箇所を見つけ出す。
  • 確実性が低く、処理を追加する判断をどうするか、などの問題がある。
  • 同時並行処理を分離しておけば、処理を追加する場所の特定が容易になる。

自動的に行うもの (P.251)

  • CGLIB, ASM などのアスペクト指向フレームワークを使うことで、手作業で追加していた処理を、プログラム的に挟み込むことができる。
  • IBM の ConTest というツールもある。

第14章 継続的改良 (P.257)

コマンドライン引数を解析するクラスを題材にして、コードが実際にリファクタリングされていく過程が説明される。

リファクタリング後のコードは、確かに読みやすいです。

第15章 JUnit の内部 (P.331)

JUnit に実在するコードを題材にして、コードがリファクタリングされていく過程が説明される。

第16章 SerialDate のリファクタリング (P.349)

JCommon というライブラリの SerialDate というクラスを題材にして、(以下略)

第17章 においと経験則

マーチン・ファウラーの「コードのにおい」に加えて、本書の筆者が考える「におい」や経験則を追加したカタログ。

コメント (P.372)

C1:不適切な情報 (P.372)

  • SVN など他のツールに任せるべき情報をコメントとして記述してはいけない。

C2:退化コメント (P.372)

  • コメントは、書いた瞬間から実装との乖離が始まる。
  • 退化したコメントはすぐに削除するか、最新化する。

C3:冗長なコメント (P.372)

  • コードに書かれていること以上の情報を与えないコメントは不要。

C4:記述不足のコメント (P.373)

  • コメントを書く際は、単語をしっかり選び、正確な文法で、読みやすく簡潔な文章で記述することを心がけなければならない。

C5:コメントアウトされたコード (P.373)

  • コメントアウトされたコードはすぐに削除する。

環境 (P.373)

E1:ビルドに複数のステップを要する (P.373)

  • 1コマンドで、プロジェクトのビルドを実行できるようにしておかなければならない。

E2:テストに複数のステップを要する (P.374)

  • 1ステップの操作で、全てのテストを実行できるようにしておかなければならない。

関数 (P.374)

F1:多すぎる引数 (P.374)

  • 関数の引数は多くても2つまで。

F2:出力引数 (P.374)

  • 出力引数は使わない。

F3:フラグ引数 (P.374)

  • フラグ引数は使わない。

F4:死んだ関数 (P.375)

  • どこからも使用されない関数は、ただちに削除すべき。

一般 (P.375)

G1:1つのソースファイルに複数の言語を使用する (P.375)

  • 1つのソースファイルは、1つのプログラミング言語で記述する。
  • やむを得ない場合でも、可能な限り少ない言語で記述できないか検討する。

※JSP は HTML, Java, JSTL, CSS, JavaScript など様々な言語で記述できてしまうので要注意。

G2:あって当然の振る舞いが実装されない (P.375)

  • 関数やクラスを作るときは、「驚き最小の原則 (The Principle of Least Surprise)」に従い、プログラマが当然と期待する振る舞いをするよう実装する。

G3:境界値に対する不正確な振る舞い (P.376)

  • 「どうせ大丈夫」という直感に頼らず、境界値を網羅したテストをしっかり記述すること。

G4:安全軽視 (P.376)

  • 失敗するテストを無視して、後で通るようにしようと自分に言い聞かせることは、自分のクレジットカードがタダでお金を生み出すものだと思い込むような間違った考え。

G5:重複 (P.376)

  • あらゆる重複は排除されるべき。

G6:抽象レベルが正しくないコード (P.377)

  • 抽象クラスには抽象レベルの高い概念を、継承クラスには抽象レベルの低い概念を持たせるようにして、しっかり抽象レベルの分離をしなければならない。
  • 抽象クラスに実装詳細に関わる定数・変数・ユーティリティ関数を置いてはいけない。

G7:継承クラスに依存したベースクラス (P.378)

  • 親クラスが子クラスに依存するようなことは、あってはならない。

G8:情報過多 (P.379)

  • クラスなどのモジュールは、内部情報の公開を最小限にしなければならない。

G9:デッドコード (P.379)

  • デッドコードを見つけたら、直ちに排除する。

G10:垂直分離 (P.379)

  • 変数は、それが使われる場所のすぐ近くで宣言すべき。
  • private 関数は、それが使用される関数の直下に配置されるべき。

G11:不整合 (P.380)

  • ある方法を採用するなら、全ての場面でその方法を適用すべき。
  • 例えば、命名方法に特定のパターンを設けるのであれば、全ての命名でそのパターンを適用すべき。

G12:雑然 (P.380)

  • 以下のようなものはゴミでしかなく、ただちに削除すべき。
    • なにもしないデフォルトコンストラクタ。
    • 未使用関数。
    • 未使用変数。
    • 追加情報のないコメント。
    • などなど。。。

G13:人為的な結合 (P.380)

  • 「とりあえず動かすため」などを理由に、本来その場所に定義すべきでないクラスや関数、変数を定義してはいけない。
  • クラス・関数・変数をどこで定義すべきかは、しっかり考えなければならない。

G14:機能の羨望 (P.381)

  • 他のクラスから何回も Getter を使って値を取得し処理をするような実装は、本来そのクラスのメソッドとして組み込まれているべき。
  • ただし、値を何回も取得するような処理が必要になる場面もある。例えば、出力用に値をフォーマットするような処理が挙げられる。

G15:セレクタ引数 (P.382)

  • 関数の振る舞いを引数の値で切り替えさせるような実装は、してはいけない。

G16:不明瞭な意図 (P.383)

  • コードは、作者の意図が明確に表現されるようにしなければならない。
  • ハンガリアン記法・マジックナンバーなどは、作者の意図を不明瞭にする。

G17:責務を持たせる場所の間違い (P.383)

  • 「驚き最小の原則」に従い、あるべき場所に宣言を配置する。

G18:不適切な static (P.384)

  • 操作を static にするかどうかは、その操作を多態的にしたくなるかどうかに依る。
  • 多態的にしたいのであれば、非 static で定義する。
  • 一般的に static よりも非 static を好むべき。迷ったら、非 static にする。

G19:説明的変数 (P.385)

  • 計算の途中結果を意図の伝わりやすい名前の変数(説明的変数)に格納することで、コードの可読性を大きく向上させることができる。
  • 説明的変数は、少ないよりかは多いほうが良い。

G20:関数名は体を表すべき (P.385)

  • 関数名を見ただけで、その関数が何をするのかが分かるように名付けなければならない。

G21:アルゴリズムを理解する (P.386)

  • テストが全て通れば実装は完了、ではない。
  • 完了と判断する前に、作成したアルゴリズムが正しいかをしっかり確認する。
  • そのためには、リファクタリングをしてコードを綺麗で表現豊かな状態にしておく必要がある。

G22:論理的な依存性を物理的なものとする (P.386)

  • 依存性には、『物理的な依存性』と「論理的な依存性」がある。
  • 『物理的な依存性』とは、あるモジュールが別のモジュールに依存している状態を言う。
  • 「論理的な依存性」とは、依存対象のモジュールが知るべき条件値(前提)を、依存しているモジュールが内部的に所持してしまっている状態を言う。
  • 『物理的な依存性』の場合、依存対象がなくなるとコンパイルエラーになる。
  • 「論理的な依存性」の場合、依存対象がなくなってもコンパイルエラーにはならない(条件値は依存しているモジュールが持っているため)。
  • 「論理的な依存性」は責務の配置ミス(G17)であり、『物理的な依存性』に置き換えるべき。

G23:if/else や switch/case よりも多態を好む (P.388)

  • switch 文の使用は、型の選択のときだけにし、それ以外は多態で処理させる。
  • すべての switch 文は疑ってかかるべき。

G24:標準の規約に従う (P.388)

  • 業界で一般的なコーディング規約に従うべき。

G25:マジックナンバーを名前付けした定数に置き換える (P.389)

  • マジックナンバーは、意図が明確な名前の定数に置き換える。
  • ただし、そのままでも意図が明確ならば、無理に定数に置き換える必要はない。

G26:正確であれ (P.390)

  • 何かコードを書くときは、常に例外的なケースを検討し、それらに対して正確な対応策を講じておかなければならない。

G27:規約より構造 (P.390)

  • 規約で強制するよりも、構造的に強制するようにした方が良い。
  • 「このクラスを追加する場合は、必ず◯◯という名前のメソッドを実装してください」と規約を設定するよりも、 abstract なメソッドを定義して構造的な制約を設けたほうが確実。

G28:条件をカプセル化せよ (P.391)

ダメな例
if (timer.hasExpired() && !timer.isRecurrent())

よりも、

良い例
if (shouldBeDeleted(timer))

の方が良い。

G29:条件の否定形を避ける (P.391)

ダメな例
if (!buffer.shouldNotCompact())

よりも、

良いな例
if (buffer.shouldCompact())

の方が良い。

G30:関数では1つのことを行うべき (P.391)

  • 関数は、1つのことを行うように分解すべき。

G31:隠れた時間軸上の結合 (P.392)

  • 関数の呼び出しに順序がある場合、それが分かるように関数を設計しなければならない。
  • 例えば、最初に実行しなければならない関数の戻り値を、次に実行しなければならない関数の引数にすることで、実行順序を縛ることができる。

G32:いいかんげんにならないこと (P.393)

  • 一時的、とりあえず動かせるように、などのいい加減な理由でコードを書いてはいけない。
  • 常に、何故そこに書くのかという根拠を持ってコードを書かなければならない。

G33:境界条件はカプセル化する (P.394)

  • 境界条件となる値は、1つの変数に格納するなどして、1カ所にまとめる。

G34:関数は1つの抽象レベルを担うべき (P.395)

  • 1つの関数の中身は、その関数より1つ下の抽象度の関数を呼び出すだけにすべき。

G35:設定可能なデータは高いレベルに置く (P.396)

  • 抽象度の高い定数やデフォルト値は、抽象度の低い関数内に定義されてはいけない。
  • 抽象度の高い値は、抽象度の高い関数から、抽象度の低い関数に、引数を使って受け渡すようにする。

G36:推移的なナビゲーションを避ける (P.397)

  • クラスAがクラスBに依存し、クラスBがクラスCに依存しているとき、クラスAはクラスCを操作しないようにする。
  • a.getB().getC().method() のようなことはしてはいけない。

Java

J1:ワイルドカードを使って、長い import のリストを避ける (P.398)

  • 同じパッケージのクラスを2つ以上使用するなら、ワイルドカードを使ってパッケージ全体をインポートするようにする。

J2:定数を継承しない (P.399)

  • 定数宣言用のインターフェースを造り、それを実装することで定数を利用できるようにするような行為はしてはいけない。
  • おとなしく、 static インポートを使用する。

J3:定数と enum (P.400)

  • int の定数よりも、 enum を使用する方が良い。
  • enum は多態を実現できるので、非常に強力。

名前

N1:記述的な名前を選ぶ (P.401)

  • 名前は、ソフトウェアの読みやすさに直結している。
  • 名前を考えるのには、しっかり時間をかけなければならない。
  • 名前を注意深く選択することで、コード上に説明を積み上げることができる。

N2:抽象レベルに適切に名前を選ぶ (P.403)

  • 実装をそのまま表すような名前を付けてはいけない。
  • そのクラス・関数の抽象レベルにふさわしい名前をつけなければならない。
  • 抽象レベルは時間経過とともに変化していくので、その時々の抽象レベルにあった名前付けが必要になる。

N3:可能な限り標準の用語を使用する (P.404)

  • 一般によく利用される用語があるのであれば、それを利用する。
  • プロジェクト内で取り決めた標準の用語を使うこともある(DDD では、これをユビキタス言語と呼ぶ)。

N4:はっきりした名前 (P.404)

  • doRename() 関数の中で renamePage() 関数が呼ばれている場合、2つの関数は名前から違いを区別することができない。
  • 名前は、はっきりと区別できるものにする。

N5:広いスコープには長い名前を (P.405)

  • 名前の長さは、スコープの広さに対応させるべき。
  • 狭いスコープでは短く簡潔な名前を、広いスコープでは長く正確な名前を付ける。

N6:エンコーディングを避ける (P.405)

  • 名前の中に、型やスコープの情報を埋め込んではいけない。

N7:名前で副作用を示すべき (P.406)

  • 関数名で示されていること以外のことを実装してはいけない。
  • 例えば、 getXXX() というメソッドが、実は値がなければ値を生成してから返す、という実装になっているのであれば、 createOrReturnXXX() のような名前にすべき。

テスト

T1:不十分なテスト (P.406)

  • どこまでテストを作成するかの条件を、「それで十分そうなら」にしてはいけない。
  • 壊れる可能性のある部分は全て対象とすべき。
  • 網羅されていない条件、検証されていない計算処理があるのなら、テストは十分とはいえない。

T2:カバレッジツールを使用する! (P.406)

  • カバレッジツールと IDE を駆使することで、テストされていないコードを簡単に見つけることができる。

T3:ささいなテストを省略しない (P.407)

  • テストがささいなら、書くことも簡単なはず。

T4:無視することを指定されたテストは、あいまいさへの問いかけである (P.407)

  • @Ignore を付けるなどしてテストを無視することで、そのテストに関する仕様が不明確であることを明示できる。

T5:境界条件テスト (P.407)

  • 境界条件のテストには最新の注意を払う。

T6:バグの周辺は徹底的にテストを (P.407)

  • バグが1つ発生したのなら、その周りも徹底的にテストしたほうが良い。

T7:失敗パターンは何かを語る (P.407)

  • テストケースの失敗パターンから問題を診断できることがある。

T8:テストカバレッジのパターンは何かを語る (P.408)

  • 実行された行と実行されなかった行の差から、テスト失敗の原因を探ることができる。

T9:テストは高速に実行できるべき (P.408)

  • テストは高速に動作するように書かなければならない。
148
140
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
148
140