Clean-Code-Notes - クリーンコードブックの私のメモ

(My notes of Clean Code book)

Created at: 2016-11-17 12:05:58
Language: NULL

クリーンコードノート

目次

第 1 章 - クリーン コード

この本は良いプログラミングについてです。それは良いコードを書く方法と、悪いコードを良いコードに変換する方法についてです。

コードは要件の詳細を表し、詳細を無視または抽象化することはできません。要件に近い言語を作成する場合があります。これらの要件を解析して正式な構造に組み立てるのに役立つツールを作成できます。しかし、必要な精度を排除することは決してありません。

なぜ悪いコードを書くのですか?

  • お急ぎですか?
  • あなたは「速く」行こうとしますか?
  • あなたは良い仕事をする時間がありませんか?
  • 同じプログラム/モジュールでの作業にうんざりしていませんか?
  • あなたの上司はあなたをすぐに終わらせるように促しますか?

前の引数は、無意味なコードの沼を作成する可能性があります。

「後で修正するために戻ってきます」と言うと、ルブランの法則「後では決して等しくない」に陥る可能性があります。

あなたは専門家であり、コードはあなたの責任です。次の逸話を分析しましょう。

あなたが医者で、時間がかかりすぎるので、手術の準備のためにすべての愚かな手洗いをやめるように要求する患者がいたらどうしますか?明らかに患者は上司です。それでも、医者は絶対に従うことを拒否すべきです。なぜでしょうか。医者は病気や感染症のリスクについて患者よりも知っているからです。医師が患者に従うことは専門家ではありません(犯罪者を気にしないでください)。

プログラマーが混乱のリスクを理解していないマネージャーの意志に屈することも専門家ではありません。

たぶん時々あなたは締め切りを作るために速く行くと思います。高速にする唯一の方法は、コードを常にできるだけクリーンに保つことです。

クリーンコードとは何ですか?

実験された各プログラマーは、クリーンコードの独自の定義を持っていますが、何かが明確であり、クリーンコードは簡単に読むことができるコードです。クリーンコードは、処理されたコードです。

彼の本の中で、ボブおじさんは次のように述べています。

この本は、クリーンコードのオブジェクトメンタースクールの説明と考えてください。その中のテクニックと教えは、私たちが芸術を実践する方法です。これらの教えに従えば、私たちが享受してきたメリットを享受し、クリーンでプロフェッショナルなコードを書くことを学ぶことができると私たちは喜んで主張します。しかし、私たちが絶対的な意味で何らかの形で「正しい」と考えるのを間違えないでください。私たちと同じくらいプロフェッショナリズムを主張している他の学校や他のマスターがいます。彼らからも学ぶのは当然のことです。

ボーイスカウトのルール

コードをうまく書くだけでは不十分です。コードは長期にわたってクリーンに保つ必要があります。私たちは皆、時間が経つにつれてコードが腐敗し、劣化するのを見てきました。したがって、私たちはこの劣化を防ぐために積極的な役割を果たす必要があります。

ボーイスカウトのルールを適用することをお勧めします

キャンプ場はいつも見つけたよりもきれいにしてください。

第2章 - 意味のある名前

名前はソフトウェアのいたるところにあります。ファイル、ディレクトリ、変数関数など私たちはそれをたくさんやっているからです。私たちはそれをうまくやったほうがいいです。

意図的な名前を使用する

名前は意図を明らかにしていると言うのは簡単です。適切な名前を選択するには時間がかかりますが、必要な名前よりも節約できます。したがって、名前に注意し、より良い名前を見つけたら名前を変更してください。

変数、関数、またはクラスの名前は、すべての大きな質問に答える必要があります。なぜそれが存在するのか、何をするのか、そしてどのように使用されるのかを教えてくれるはずです。名前にコメントが必要な場合、その名前はその意図を明らかにしません

意図を明らかにしない 意図を明らかにする
int d; // elapsed time in days
int elapsedTimeInDays

意図がわかる名前を選択すると、コードの理解と変更がはるかに簡単になります。例:

public List<int[]> getThem() {
  List<int[]> list1 = new ArrayList<int[]>();
  for (int[] x : theList)
    if (x[0] == 4)
      list1.add(x);
  return list1;
}

このコードは単純ですが、多くの質問を作成します。

  1. の内容は何ですか?
    theList
  2. リスト内のアイテムの意味は何ですか?.
    x[0]
  3. なぜ比較するのか?
    x[0]
    4
  4. 返されたリストをどのように使用しますか?

これらの質問に対する回答はコード サンプルには含まれていませんが、存在する可能性があります。掃海艇ゲームで作業しているとします。前のコードは次のようにリファクタリングできます。

public List<int[]> getFlaggedCells() {
  List<int[]> flaggedCells = new ArrayList<int[]>();
  for (int[] cell : gameBoard)
    if (cell[STATUS_VALUE] == FLAGGED)
      flaggedCells.add(cell);
  return flaggedCells;
}

これで、次の情報がわかりました。

  1. theList
    は、
    gameBoard
  2. x[0]
    ボード内のセルを表し、フラグが設定されたセルを表します
    4
  3. 返されるリストは、
    flaggedCells

コードの単純さは変わっていないことに注意してください。それでも、まったく同じ数の演算子と定数があり、入れ子レベルの数はまったく同じです。しかし、コードははるかに明確になりました。

の配列を使用する代わりに、セルの単純なクラスを書くコードを改善できます。マジックナンバーを隠すための意図を明らかにする関数(と呼ばれる)を含めることができます。その結果、関数の新しい関数が生成されます。

ints
isFlagged

public List<Cell> getFlaggedCells() {
  List<Cell> flaggedCells = new ArrayList<Cell>();
  for (Cell cell : gameBoard)
    if (cell.isFlagged())
      flaggedCells.add(cell);
  return flaggedCells;
}

偽情報を避ける

プログラマーは、コードの意味を曖昧にする誤った手がかりを残さないようにする必要があります。定着した意味が意図した意味と異なる言葉は避けるべきです。

アカウントのグループ化を、実際には .この言葉はプログラマーに固有のものを意味します。アカウントを保持するコンテナが実際にはリストでない場合、誤った結論につながる可能性があります。だから、または単に単純な方が良いでしょう。

accountList
List
List
accountGroup
bunchOfAccounts
accounts

少しずつ異なる名前の使用に注意してください。1つのモジュールと、もう少し離れた場所の微妙な違いを見つけるのにどれくらい時間がかかりますか?言葉は恐ろしく似た形をしています

XYZControllerForEfficientHandlingOfStrings
XYZControllerForEfficientStorageOfStrings

意味のある区別をする

プログラマは、コンパイラまたはインタプリタを満足させるためだけにコードを記述するときに、自分で問題を作成します。たとえば、同じ名前を使用して同じスコープ内の 2 つの異なるものを参照することはできないため、任意の方法で 1 つの名前を変更したくなるかもしれません。これはスペルミスによって行われることがあり、スペルミスを修正するとコンパイルできなくなるという驚くべき状況につながります。たとえば、名前が他の何かに使用されたために変数を作成します。

klass
class

次の関数では、引数は有益ではなく、作成者の意図の手がかりを提供しません。

a1
a2

public static void copyChars(char a1[], char a2[]) {
  for (int i = 0; i < a1.length; i++) {
    a2[i] = a1[i];
  }
}

より明示的な引数名を選択するコードを改善できます。

public static void copyChars(char source[], char destination[]) {
  for (int i = 0; i < source.length; i++) {
    destination[i] = source[i];
  }
}

ノイズワードは別の無意味な区別です。Product クラスがあるとします。別の呼び出しまたはがある場合は、名前を異なる意味にすることなく、名前を異なるものにしました。情報とデータは、a、an、theなどの不明瞭なノイズワードです。

ProductInfo
ProductData

ノイズワードは冗長です。変数名に variable という単語を使用しないでください。テーブル名に table という単語を使用しないでください。

発音可能な名前を使用する

変数(生成日、年、月、日、時、分、秒)があり、この変数について「なぜemm dee aich emm ess」と呼ぶ必要がある会話を想像してみてください。次のようなクラスを変換することを検討できます。

genymdhms

class DtaRcrd102 {
  private Date genymdhms;
  private Date modymdhms;
  private final String pszqint = "102";
  /* ... */
};

宛先

class Customer {
  private Date generationTimestamp;
  private Date modificationTimestamp;;
  private final String recordId = "102";
  /* ... */
};

検索可能な名前を使用する

1 文字の名前と数値定数には、テキスト本文全体で見つけにくいという特定の問題があります。

エンコードを避ける

負担を増やすことなく対処するのに十分なエンコーディングがあります。型またはスコープの情報を名前にエンコードすると、解読の負担が増すだけです。エンコードされた名前は発音できることはほとんどなく、入力を間違えやすくなります。この例としては、ハンガリー記法の使用やメンバー接頭辞の使用があります。

インターフェイスと実装

これらは、エンコーディングの特殊なケースである場合があります。たとえば、図形を作成するための抽象ファクトリを構築しているとします。このファクトリはインターフェイスであり、具象クラスによって実装されます。あなたはそれらに何と名前を付けるべきですか? そして。インターフェイスは装飾しない方が望ましいです。私はユーザーに私が彼らにインターフェースを渡していることを知られたくありません。私はそれが.したがって、インターフェイスまたは実装のいずれかをエンコードする必要がある場合は、実装を選択します。それを呼び出す 、または恐ろしい 、インターフェイスをエンコードするよりも望ましいです。

IShapeFactory
ShapeFactory
ShapeFactory
ShapeFactoryImp
CShapeFactory

メンタルマッピングを避ける

読者はあなたの名前を彼らがすでに知っている他の名前に精神的に翻訳する必要はありません。

スマートプログラマーとプロのプログラマーの違いの1つは、プロが明快さが王様であることを理解していることです。専門家は自分の力を善のために使い、他の人が理解できるコードを書きます。

クラス名

クラスとオブジェクトには、などの名詞または名詞句名が必要です。,, , やクラスの名前などの単語は避けてください。クラス名は動詞であってはなりません。

Customer
WikiPage
Account
AddressParser
Manager
Processor
Data
Info

メソッド名

メソッドには、 や のような動詞または動詞句名が必要です。アクセサ、ミューテータ、および述部は、その値にちなんで名前を付け、接頭辞 get と set を付ける必要があり、javabean 標準に準拠しています。

postPayment
deletePage
save

コンストラクターがオーバーロードされている場合は、引数を記述する名前を持つ静的ファクトリ メソッドを使用します。例えば:

Complex fulcrumPoint = Complex.FromRealNumber(23.0);

一般的によりも優れています

Complex fulcrumPoint = new Complex(23.0);

対応するコンストラクターをプライベートにすることで、それらの使用を強制することを検討してください。

可愛くないで

かわいい名前 クリーンな名前
holyHandGranade
deleteItems
whack
kill
eatMyShorts
abort

コンセプトごとに1つの単語を選ぶ

1つの抽象的な概念に対して1つの単語を選び、それに固執します。たとえば、fetch、retrieve、およびgetを異なるクラスの同等のメソッドとして使用するのは混乱を招きます。

駄洒落しないでください

2つの目的で同じ単語を使用することは避けてください。2つの異なるアイデアに同じ用語を使用することは、本質的に駄洒落です。

例:2つの既存の値を追加または連結して新しい値を作成するクラスの使用と、コレクションに単純なパラメータを配置するための別のクラスの使用では、代わりにorのような名前を使用する方が良いオプションです。

add
add
insert
append

ソリューション ドメイン名の使用

あなたのコードを読む人はプログラマーになることを忘れないでください。したがって、先に進んで、コンピュータサイエンス(CS)用語、アルゴリズム名、パターン名、数学用語などを使用します。

問題のあるドメイン名を使用する

あなたがしていることに「プログラマー」がない場合は、問題のあるドメインの名前を使用してください。少なくともあなたのコードを保守しているプログラマーは、それが何を意味するのかをドメインの専門家に尋ねることができます。

意味のあるコンテキストを追加する

それ自体が意味のある名前がいくつかありますが、ほとんどは意味がありません。代わりに、適切な名前のクラス、関数、または名前空間で名前を囲むことによって、読者のコンテキストに名前を配置する必要があります。他のすべてが失敗した場合は、最後の手段として名前の前にを付ける必要があるかもしれません

のような変数: , , , , , .まとめると、それらがアドレスを形成することは明らかですが、変数の状態がメソッドで単独で使用されているのを見た場合は、次のようなプレフィックスを使用してコンテキストを追加できます。 少なくとも読者は、変数が大きな構造の一部であることを理解するでしょう。もちろん、より良い解決策は、変数がより大きな概念に属していることをコンパイラが知っているという名前のクラスを作成することです

firstName
lastName
street
city
state
addrState
Address

不必要なコンテキストを追加しないでください

「ガソリンスタンドデラックス」と呼ばれる架空のアプリケーションでは、すべてのクラスにGSDを付けることは悪い考えです。例:

GSDAccountAddress

明確である限り、短い名前は一般的に長い名前よりも優れています。必要以上のコンテキストを名前に追加しないでください。

第 3 章 - 関数

関数は、あらゆるトピックの編成の最初の行です。

小さい!!

関数の最初のルールは、それらが小さくなければならないということです。関数の2番目のルールは、それよりも小さくする必要があるということです。

ブロックとインデント

これは、ステートメント、ステートメント、ステートメントなどの中のブロックが 1 行の長さである必要があることを意味します。おそらくその行は関数呼び出しでなければなりません。これにより、囲む関数が小さく保たれるだけでなく、ブロック内で呼び出される関数がわかりやすい名前を付けることができるため、ドキュメントの価値も追加されます。

if
else
while

これは、関数が入れ子になった構造を保持するのに十分な大きさであってはならないことも意味します。したがって、関数のインデント レベルは 1 または 2 より大きくすることはできません。もちろん、これにより、関数が読みやすく理解しやすくなります。

一つのことをする

関数は1つのことを行う必要があります。彼らはそれをうまくやるべきです。彼らはそれだけをするべきです。

関数内のセクション

宣言初期化などのセクションに分割された関数がある場合、関数が複数のことを行っていることは明らかな症状です。1つのことを行う機能は、合理的にセクションに分割することはできません。

関数ごとに 1 つの抽象化レベル

関数が「1つのこと」を行っていることを確認するには、関数内のステートメントがすべて同じ抽象化レベルにあることを確認する必要があります。

コードを上から下に読む: ステップダウンルール

コードをトップダウンの物語のように読みたいのです。5 すべての関数の後に次の抽象化レベルの関数が続くようにして、プログラムを読み、関数のリストを読みながら一度に1レベルの抽象化を降ろします。

別の言い方をすれば、プログラムをセットのように読めるようにしたい のTO段落で、それぞれが現在の抽象化レベルを記述し、次のレベルの後続のTO段落を参照しています。

- To include the setups and teardowns, we include setups, then we include the test page content, and then we include the teardowns.
- To include the setups, we include the suite setup if this is a suite, then we include the regular setup.
- To include the suite setup, we search the parent hierarchy for the “SuiteSetUp” page and add an include statement with the path of that page.
- To search the parent...

プログラマーがこの規則に従い、単一の抽象化レベルにとどまる関数を書くことを学ぶことは非常に難しいことがわかりました。しかし、このトリックを学ぶことも非常に重要です。これは、機能を短くし、「1つのこと」を確実に実行するための鍵です。コードをTO段落のトップダウンセットのように読むことは、抽象化レベルの一貫性を維持するための効果的な手法です。

スイッチ ステートメント

小さなスイッチステートメントを作成するのは難しいです。6 2つのケースしかないswitchステートメントでさえ、1つのブロックまたは関数よりも大きくなります。また、1つのことを行うswitchステートメントを作成するのも困難です。その性質上、switch ステートメントは常に N 個のことを行います。残念ながら、switch ステートメントを常に回避できるわけではありませんが、各 switch ステートメントが低レベルのクラスに埋め込まれ、繰り返されないようにすることはできます。もちろん、これは多型で行います。

わかりやすい名前を使用する

クリーンなコードに取り組んでいるのは、各ルーチンが期待どおりであることが判明したときです。

その原則を達成するための戦いの半分は、1つのことを行う小さな関数に適した名前を選択することです。関数が小さく、焦点が絞られているほど、わかりやすい名前を選択しやすくなります。

名前を長くすることを恐れないでください。長い説明的な名前は、短い謎めいた名前よりも優れています。長い説明的な名前は、長い説明的なコメントよりも優れています。関数名で複数の単語を簡単に読み取ることができる命名規則を使用し、それらの複数の単語を使用して、関数にその機能を示す名前を付けます。

わかりやすい名前を選択すると、頭の中でモジュールの設計が明確になり、改善に役立ちます。良い名前を探すと、コードが有利に再構築されることは珍しくありません。

関数の引数

関数の引数の理想的な数はゼロ(ニラディック)です。次は1つ(モナディック)で、続いて2つ(ダイアディック)が続きます。3つの引数(トライアディック)は、可能な限り避ける必要があります。3つ以上(ポリアディック)は非常に特別な正当化が必要であり、とにかく使用しないでください。

テストの観点からは、議論はさらに困難です。引数のさまざまな組み合わせがすべて正しく機能することを確認するために、すべてのテストケースを作成することの難しさを想像してみてください。引数がない場合、これは簡単です。議論が1つあれば、それほど難しくはありません。2つの引数があると、問題はもう少し難しくなります。引数が 2 つを超える場合、適切な値のすべての組み合わせをテストするのは困難な場合があります。

出力引数は、入力引数よりも理解しにくいです。関数を読み取るとき、私たちは引数を介して関数に入り、戻り値を介して情報が関数に入るという考えに慣れています。私たちは通常、議論を通じて情報が出ることを期待していません。したがって、出力引数はしばしばダブルテイクを行います。

一般的なモナド形式

1 つの引数を関数に渡す一般的な理由は 2 つあります。のように、その議論について質問しているかもしれません。あるいは、その議論を操作して、それを別のものに変換して返すかもしれません。たとえば、ファイル名を戻り値に変換します。これらの2つの使用法は、読者が関数を見たときに期待するものです。区別を明確にする名前を選択し、常に一貫したコンテキストで 2 つの形式を使用する必要があります。

boolean fileExists(“MyFile”)
InputStream fileOpen(“MyFile”)
String
InputStream

フラグ引数

フラグ引数は醜いです。ブール値を関数に渡すことは本当にひどい習慣です。それはすぐにメソッドのシグネチャを複雑にし、この関数が複数のことを行うことを大声で宣言します。フラグが1つのことを行い、フラグが !

true
false

ダイアディック関数

2つの引数を持つ関数は、モナド関数よりも理解が困難です。たとえば、 より理解しやすい

writeField(name)
writeField(output-Stream, name)

もちろん、2つの引数が適切な場合もあります。たとえば、完全に合理的です。デカルト点は当然2つの議論を取ります。

Point p = new Point(0,0);

assertEquals(期待、実際)のような明らかなダイアディック関数でさえ問題があります。期待される場所に実際の場所を何回置いたか?2 つの引数には自然な順序はありません。予想される実際の順序付けは、学習するために練習を必要とする規則です。

ダイアドは悪ではありません、そしてあなたは確かにそれらを書かなければならないでしょう。ただし、それらにはコストがかかることに注意する必要があり、それらをモナドに変換するために利用できる可能性のあるメカニズムを利用する必要があります。たとえば、writeField メソッドを outputStream のメンバーにして、outputStream と言うことができます。書くフィールド(名前) .または、outputStream を現在のクラスのメンバー変数にして、渡す必要がないようにすることもできます。または、コンストラクターで出力ストリームを受け取り、書き込みメソッドを持つ FieldWriter のような新しいクラスを抽出することもできます。

トライアド

3つの引数を取る関数は、ダイアドよりも理解するのがかなり難しいです。順序付け、一時停止、無視の問題は 2 倍以上になります。トライアドを作成する前に、慎重に検討することをお勧めします。

引数オブジェクト

比べる:

Circle makeCircle(double x, double y, double radius);

Circle makeCircle(Point center, double radius);

動詞とキーワード

関数に適した名前を選択することは、関数の意図と引数の順序と意図を説明するのに大いに役立ちます。モナドの場合、関数と引数は非常に素晴らしい動詞/名詞のペアを形成する必要があります。たとえば、非常に刺激的です。この「名前」が何であれ、それは「書かれている」のです。さらに良い名前は、「名前」のものが「フィールド」であることを示しています。

write(name)
writeField(name)

この最後は、関数名のキーワード形式の例です。この形式を使用して、引数の名前を関数名にエンコードします。たとえば、と記述した方がよい場合があります。これにより、引数の順序を覚えておく必要があるという問題が大幅に軽減されます。

assertEquals
assertExpectedEqualsActual(expected, actual)

出力引数

一般に、出力引数は避けるべきです。関数が何かの状態を変更する必要がある場合は、所有するオブジェクトの状態を変更します。

コマンド クエリの分離

関数は何かを行うか、何かに答える必要がありますが、両方ではありません。関数はオブジェクトの状態を変更するか、そのオブジェクトに関する情報を返す必要があります。両方を行うと、混乱を招くことがよくあります。

エラーコードを返すよりも例外を優先する

コマンド関数からエラー コードを返すことは、コマンド クエリの分離の微妙な違反です。

繰り返さないでください

複製は、ソフトウェアのすべての悪の根源である可能性があります。それを制御または排除する目的で、多くの原則と実践が作成されています。

構造化プログラミング

一部のプログラマーは、Edsger Dijkstraの構造化プログラミングのルールに従います。ダイクストラは、すべての関数と関数内のすべてのブロックは、1つの入口と1つの出口を持つべきだと述べました。これらの規則に従うということは、関数内に return ステートメントが 1 つだけ存在し、ループ内に no またはステートメントが存在し、ステートメントが決して存在しないことを意味します。

break
continue
goto

私たちは構造化プログラミングの目標と分野に共感していますが、関数が非常に小さい場合、これらのルールはほとんど役に立ちません。そのようなルールが大きな利益をもたらすのは、より大きな機能においてのみです。

したがって、関数を小さく保つと、時折複数の、、またはステートメントが害を及ぼすことはなく、単一エントリ、単一出口のルールよりも表現力豊かになることがあります。一方、大きな関数でのみ意味をなすので、避けるべきです

return
break
continue
goto

第 4 章 - コメント

適切に配置されたコメントほど役立つものはありません。軽薄な独断的なコメントほどモジュールを乱雑にすることはできません。嘘や誤った情報を広める古いコメントほど有害なものはありません。

私たちのプログラミング言語が十分に表現力豊かであったり、それらの言語を微妙に使って意図を表現する才能があれば、コメントはそれほど必要ないでしょう。

コメントは悪いコードを補わない

コメントの少ない明確で表現力豊かなコードは、コメントの多い雑然とした複雑なコードよりもはるかに優れています。あなたが作った混乱を説明するコメントを書くことに時間を費やすのではなく、その混乱をきれいにすることに時間を費やしてください。

コードで自分自身を説明する

// Check to see if the employee is eligible for full benefits
if ((employee.flags & HOURLY_FLAG) && (employee.age > 65))

if (employee.isEligibleForFullBenefits())

良いコメント

いくつかのコメントは必要または有益です。しかし、唯一の本当に良いコメントは、あなたが書かない方法を見つけたコメントです。

法的コメント

企業のコーディング標準では、法的な理由で特定のコメントを書く必要がある場合があります。たとえば、著作権と著者の声明は、各ソースファイルの先頭にあるコメントに入れるために必要かつ合理的なものです。

有益なコメント

コメントで基本的な情報を提供すると便利な場合があります。たとえば、抽象メソッドの戻り値を説明する次のコメントについて考えてみます。

// Returns an instance of the Responder being tested.
protected abstract Responder responderInstance();

このようなコメントが役立つ場合もありますが、可能な場合は関数の名前を使用して情報を伝えることをお勧めします。たとえば、この場合、関数の名前を変更することでコメントを冗長にすることができます。

responderBeingTested

意図の説明

コメントは、実装に関する有用な情報だけでなく、決定の背後にある意図を提供する場合があります。例:

public int compareTo(Object o)
{
  if(o instanceof WikiPagePath)
  {
    WikiPagePath p = (WikiPagePath) o;
    String compressedName = StringUtil.join(names, "");
    String compressedArgumentName = StringUtil.join(p.names, "");
    return compressedName.compareTo(compressedArgumentName);
  }
  return 1; // we are greater because we are the right type.
}

解明

あいまいな引数や戻り値の意味を読みやすいものに変換すると便利な場合があります。一般に、その引数または戻り値をそれ自体で明確にする方法を見つけることをお勧めします。しかし、標準ライブラリの一部である場合、または変更できないコードの場合は、役立つ明確なコメントが役立ちます。

譲歩の警告

特定の結果について他のプログラマーに警告すると便利な場合があります。

// Don't run unless you
// have some time to kill.
public void _testWithReallyBigFile() {
  writeLinesToFile(10000000);
  response.setBody(testFile);
  response.readyToSend(this);
  String responseString = output.toString();
  assertSubString("Content-Length: 1000000000", responseString);
  assertTrue(bytesSent > 1000000000);
}

藤堂コメント

「To Do」メモを //TODO コメントの形式で残すのが妥当な場合があります。の 次のケースでは、TODOコメントは、関数が縮退した実装を持っている理由とその関数の将来について説明します。

//TODO-MdM these are not needed
// We expect this to go away when we do the checkout model
protected VersionInfo makeVersion() throws Exception {
  return null;
}

TODOは、プログラマーが実行する必要があると考えているジョブですが、何らかの理由で現時点では実行できません。非推奨の機能を削除するように促したり、他のユーザーに問題を見てもらうように嘆願したりする場合があります。これは、他の誰かにもっと良い名前を考えてもらうための要求や、計画されたイベントに依存する変更を加えるためのリマインダーである可能性があります。TODOが他に何であれ、システムに悪いコードを残す言い訳にはなりません。

増幅

コメントは、他の方法では取るに足らないように見えるかもしれない何かの重要性を増幅するために使用される場合があります。

String listItemContent = match.group(3).trim();
// the trim is real important. It removes the starting
// spaces that could cause the item to be recognized
// as another list.
new ListItemWidget(this, listItemContent, this.level + 1);
return buildList(text.substring(match.end()));

パブリック API の Javadocs

よく説明されたパブリックAPIほど有用で満足のいくものはありません。標準 Java ライブラリの javadoc はその好例です。それらなしでJavaプログラムを書くことはせいぜい難しいでしょう。

悪いコメント

ほとんどのコメントはこのカテゴリに分類されます。通常、それらは松葉杖や貧弱なコードの言い訳、または不十分な決定の正当化であり、プログラマーが独り言を言っているだけです。

ぶつぶつ

あなたがそうすべきだと感じたから、またはプロセスがそれを必要とするという理由だけでコメントを突っ込むことは、ハックです。コメントを書くことにした場合は、それがあなたが書くことができる最高のコメントであることを確認するために必要な時間を費やしてください。例:

public void loadProperties() {

  try {
    String propertiesPath = propertiesLocation + "/" + PROPERTIES_FILE;
    FileInputStream propertiesStream = new FileInputStream(propertiesPath);
    loadedProperties.load(propertiesStream);
  }
  catch(IOException e) {
    // No properties files means all defaults are loaded
  }
}

catchブロックのコメントはどういう意味ですか?明らかに作者にとって何かを意味していましたが、意味はそれほどうまくいきません。どうやら、私たちが得た場合、それはプロパティファイルがなかったことを意味します。その場合、すべてのデフォルトがロードされます。しかし、誰がすべてのデフォルトをロードしますか?

IOException

冗長なコメント

// Utility method that returns when this.closed is true. Throws an exception
// if the timeout is reached.
public synchronized void waitForClose(final long timeoutMillis) throws Exception {
  if(!closed) {
    wait(timeoutMillis);
    if(!closed)
      throw new Exception("MockResponseSender could not be closed");
  }
}

このコメントはどのような目的に役立ちますか?それは確かにコードよりも有益ではありません。コードを正当化したり、意図や理論的根拠を提供したりすることはありません。コードよりも読みやすくありません。確かに、それはコードよりも正確ではなく、真の理解の代わりにその精度の欠如を受け入れるように読者を誘惑します。

誤解を招くコメント

時々、すべての最善の意図で、プログラマーは彼のコメントで正確であるのに十分正確ではない声明を出します。前のセクションの例をもう一度考えてみましょう。メソッドは、 になったときに返されません。次の場合を返します。それ以外の場合は、ブラインド タイムアウトを待機し、それでも true でない場合は例外をスローします。

this.closed
true
this.closed
true
this.closed

必須コメント

すべての関数にjavadocが必要である、またはすべての変数にコメントが必要であるというルールがあるのは、まったくばかげています。このようなコメントは、コードを乱雑にし、嘘を広め、一般的な混乱と混乱を助長します。

/**
*
* @param title The title of the CD
* @param author The author of the CD
* @param tracks The number of tracks on the CD
* @param durationInMinutes The duration of the CD in minutes
*/
public void addCD(String title, String author, int tracks, int durationInMinutes) {
  CD cd = new CD();
  cd.title = title;
  cd.author = author;
  cd.tracks = tracks;
  cd.duration = duration;
  cdList.add(cd);
}

ジャーナルコメント

モジュールを編集するたびに、モジュールの先頭にコメントを追加することがあります。例:

* Changes (from 11-Oct-2001)
* --------------------------
* 11-Oct-2001 : Re-organised the class and moved it to new package com.jrefinery.date (DG);
* 05-Nov-2001 : Added a getDescription() method, and eliminated NotableDate class (DG);
* 12-Nov-2001 : IBD requires setDescription() method, now that NotableDate class is gone (DG); Changed getPreviousDayOfWeek(),
getFollowingDayOfWeek() and getNearestDayOfWeek() to correct bugs (DG);
* 05-Dec-2001 : Fixed bug in SpreadsheetDate class (DG);

今日、私たちはソースコード管理システムを持っています、私たちはこのタイプのログを必要としません。

ノイズコメント

次の例のコメントは、新しい情報を提供していません。

/**
* Default constructor.
*/
protected AnnualDateRule() {
}
/** The day of the month. */
private int dayOfMonth;

Javadocsのコメントはこのカテゴリに入ることができます。多くの場合、それらはドキュメントを提供したいという見当違いの欲求から書かれた冗長なノイズの多いコメントです。

関数または変数を使用できる場合はコメントを使用しないでください

例:

// does the module from the global list <mod> depend on the
// subsystem we are part of?
if (smodule.getDependSubsystems().contains(subSysMod.getSubSystem()))

ArrayList moduleDependees = smodule.getDependSubsystems();
String ourSubSystem = subSysMod.getSubSystem();
if (moduleDependees.contains(ourSubSystem))

位置マーカー

このタイプのコメントは騒々しいです

// Actions //////////////////////////////////

閉じ中括弧コメント

例:

public class wc {
  public static void main(String[] args) {
    BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
    String line;
    int lineCount = 0;
    int charCount = 0;
    int wordCount = 0;
    try {
      while ((line = in.readLine()) != null) {
        lineCount++;
        charCount += line.length();
        String words[] = line.split("\\W");
        wordCount += words.length;

      } //while
      System.out.println("wordCount = " + wordCount);
      System.out.println("lineCount = " + lineCount);
      System.out.println("charCount = " + charCount);

    } // try
    catch (IOException e) {
      System.err.println("Error:" + e.getMessage());

    } //catch

  } //main

代わりに、小さな関数でコードを分割して、このタイプのコメントを使用することもできます。

帰属と署名

例:

/* Added by Rick */

代わりに、VCS はこの情報を管理できます。

コメント アウトされたコード

InputStreamResponse response = new InputStreamResponse();
response.setBody(formatter.getResultStream(), formatter.getByteCount());
// InputStream resultsStream = formatter.getResultStream();
// StreamReader reader = new StreamReader(resultsStream);
// response.setContent(reader.read(formatter.getByteCount()));

もう必要ない場合は削除してください、もう一度必要になった場合は、後でVCSで戻ることができます。

HTML コメント

ソースコードのコメントのHTMLは、以下のコードを読むことでわかるように、忌まわしいものです。

/**
* Task to run fit tests.
* This task runs fitnesse tests and publishes the results.
* <p/>
* <pre>
* Usage:
* &lt;taskdef name=&quot;execute-fitnesse-tests&quot;
* classname=&quot;fitnesse.ant.ExecuteFitnesseTestsTask&quot;
* classpathref=&quot;classpath&quot; /&gt;
* OR
* &lt;taskdef classpathref=&quot;classpath&quot;
* resource=&quot;tasks.properties&quot; /&gt;
* <p/>
* &lt;execute-fitnesse-tests
* suitepage=&quot;FitNesse.SuiteAcceptanceTests&quot;
* fitnesseport=&quot;8082&quot;
* resultsdir=&quot;${results.dir}&quot;
* resultshtmlpage=&quot;fit-results.html&quot;
* classpathref=&quot;classpath&quot; /&gt;
* </pre>
*/

非ローカル情報

コメントを書く必要がある場合は、近くに表示されるコードを説明していることを確認してください。ローカルコメントのコンテキストでシステム全体の情報を提供しないでください。

情報が多すぎる

興味深い歴史的な議論や詳細の無関係な説明をコメントに入れないでください。

不明瞭な接続

コメントとそれが記述するコードとの関係は明白でなければなりません。コメントを書くのに苦労するつもりなら、少なくとも読者がコメントとコードを見て、コメントが何について話しているのかを理解できるようにしたいと思います

関数ヘッダー

短い関数はあまり説明を必要としません。1つのことを行う小さな関数の適切に選択された名前は、通常、コメントヘッダーよりも優れています。

非公開コードのJavadoc

JavadocはパブリックAPI用であり、非公開コードではヘルプよりも気を散らす可能性があります。

第 5 章 - 書式設定

コードの書式設定は重要です。無視することはあまりにも重要であり、宗教的に扱うことはあまりにも重要です。コードの書式設定はコミュニケーションに関するものであり、コミュニケーションはプロの開発者の最初の仕事です。

縦書きの書式設定

概念間の垂直方向の開放性

この概念は、コード内で概念を分離する方法にあり、次の例ではそれを理解できます。

package fitnesse.wikitext.widgets;

import java.util.regex.*;

public class BoldWidget extends ParentWidget {
  public static final String REGEXP = "'''.+?'''";
  private static final Pattern pattern = Pattern.compile("'''(.+?)'''",
      Pattern.MULTILINE + Pattern.DOTALL
      );

  public BoldWidget(ParentWidget parent, String text) throws Exception {
    super(parent);
    Matcher match = pattern.matcher(text);
    match.find();
    addChildWidgets(match.group(1));
  }

  public String render() throws Exception {
    StringBuffer html = new StringBuffer("<b>");
    html.append(childHtml()).append("</b>");
    return html.toString();
  }
}
package fitnesse.wikitext.widgets;
import java.util.regex.*;
public class BoldWidget extends ParentWidget {
  public static final String REGEXP = "'''.+?'''";
  private static final Pattern pattern = Pattern.compile("'''(.+?)'''",
  Pattern.MULTILINE + Pattern.DOTALL);
  public BoldWidget(ParentWidget parent, String text) throws Exception {
    super(parent);
    Matcher match = pattern.matcher(text); match.find(); addChildWidgets(match.group(1));
  }
  public String render() throws Exception { StringBuffer html = new StringBuffer("<b>"); html.append(childHtml()).append("</b>"); return html.toString();
  }
}

ご覧のとおり、最初の例の読みやすさは2番目の例の読みやすさよりも優れています。

垂直密度

垂直密度は密接な関連を意味します。したがって、密接に関連するコード行は垂直方向に密集して表示されるはずです。次の例を確認してください。

public class ReporterConfig {

	/**
	 * The class name of the reporter listener */
	private String m_className;

	/**
	 * The properties of the reporter listener */
	private List<Property> m_properties = new ArrayList<Property>();

	public void addProperty(Property property) { m_properties.add(property);
	}
public class ReporterConfig {
  private String m_className;
  private List<Property> m_properties = new ArrayList<Property>();

  public void addProperty(Property property) {
    m_properties.add(property);
  }
}

2番目のコードは読みやすくなっています。それは「目いっぱい」に収まります。

垂直距離

変数宣言。変数は、できるだけその使用法に近い状態で宣言する必要があります。関数は非常に短いため、ローカル変数は各関数の上部に表示されます。

一方、インスタンス変数は、クラスの先頭で宣言する必要があります。適切に設計されたクラスでは、クラスのメソッドのすべてではないにしても多くの変数で使用されるため、これらの変数の垂直距離は増加しません。

インスタンス変数がどこに行くべきかについては多くの議論がありました。C ++では、すべてのインスタンス変数を一番下に置く、いわゆるはさみルールを一般的に実践していました。ただし、Javaの一般的な規則は、それらすべてをクラスの一番上に置くことです。他の慣習に従う理由は見当たりません。重要なことは、インスタンス変数を1つのよく知られた場所で宣言することです。誰もが宣言を見るためにどこに行くべきかを知っているべきです。

依存関数。ある関数が別の関数を呼び出す場合は、垂直方向に近く、可能であれば呼び出し元が呼び出し先の上にいる必要があります。これにより、プログラムに自然な流れが与えられます。規則に確実に従えば、読者は関数定義が使用後すぐに続くことを信頼できます。

概念的なアフィニティ。コードの特定のビットは、他のビットの近くに配置する必要があります。彼らは特定の概念的な親和性を持っています。その親和性が強いほど、それらの間の垂直距離は短くなります。

垂直方向の順序付け

一般に、関数呼び出しの依存関係を下方向を指すようにします。つまり、呼び出される関数は、呼び出しを行う関数の下にある必要があります。これにより、ソースコードモジュールを高レベルから低レベルにうまく流れさせることができます。(これは、Pascal、C、C ++などの言語とは正反対で、関数を使用する前に定義するか、少なくとも宣言する必要があります)

水平方向の書式設定

水平方向の開放性と密度

水平方向の空白を使用して、強く関連するものを関連付け、より弱い関連性のあるものの関連付けを解除します。例:

private void measureLine(String line) {
  lineCount++;
  int lineSize = line.length();
  totalChars += lineSize;
  lineWidthHistogram.addLine(lineSize, lineCount);
  recordWidestLine(lineSize);
}

代入ステートメントには、左側と右側という 2 つの異なる主要な要素があります。スペースはその分離を明確にします。

水平方向の配置

public class Example implements Base
{
  private   Socket      socket;
  private   inputStream input;
  protected long        requestProgress;

  public Expediter(Socket      s,
                   inputStream input) {
    this.socket =     s;
    this.input  =     input;
  }
}

現代の言語では、このタイプの配置は役に立ちません。アライメントは間違ったことを強調しているようで、私の目を本当の意図からそらします。

public class Example implements Base
{
  private Socket socket;
  private inputStream input;
  protected longrequestProgress;

  public Expediter(Socket s, inputStream input) {

    this.socket = s;
    this.input = input;
  }
}

これはより良いアプローチです。

圧入

インデント 目に見える階層と明確に定義されたブロックを持つのに役立つため、重要です。

チームルール

すべてのプログラマーは自分のお気に入りのフォーマットルールを持っていますが、彼がチームで働いている場合、チームはルールします。

開発者チームは 1 つの書式設定スタイルに同意し、そのチームのすべてのメンバーはそのスタイルを使用する必要があります。私たちは、ソフトウェアに一貫したスタイルを持たせたいと考えています。私たちは、それが反対する個人の束によって書かれたように見せたくありません。

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

データ抽象化

実装を隠すことは、変数の間に関数の層を置くことだけの問題ではありません。実装を隠すことは抽象化についてです!クラスは、単にゲッターとセッターを介して変数をプッシュするだけではありません。むしろ、ユーザーがその実装を知らなくてもデータの本質を操作できるようにする抽象インターフェイスを公開します。

データ/オブジェクトの非対称性

これら 2 つの例は、オブジェクトとデータ構造の違いを示しています。オブジェクトは抽象化の背後にデータを隠し、そのデータを操作する関数を公開します。データ構造はデータを公開し、意味のある機能はありません。

プロシージャルシェイプ

public class Square {
  public Point topLeft;
  public double side;
}

public class Rectangle {
  public Point topLeft;
  public double height;
  public double width;
}

public class Circle {
  public Point center;
  public double radius;
}

public class Geometry {
  public final double PI = 3.141592653589793;

  public double area(Object shape) throws NoSuchShapeException {
    if (shape instanceof Square) {
      Square s = (Square)shape;
      return s.side * s.side;
    }
    else if (shape instanceof Rectangle) { Rectangle r = (Rectangle)shape; return r.height * r.width;
    }
    else if (shape instanceof Circle) {
      Circle c = (Circle)shape;
      return PI * c.radius * c.radius;
    }
    throw new NoSuchShapeException();
  }
}

ポリモーフィック形状

public class Square implements Shape {
  private Point topLeft;
  private double side;

  public double area() {
    return side*side;
  }
}

public class Rectangle implements Shape {
  private Point topLeft;
  private double height;
  private double width;

  public double area() {
    return height * width;
  }
}

public class Circle implements Shape {
  private Point center;
  private double radius;
  public final double PI = 3.141592653589793;

  public double area() {
    return PI * radius * radius;
  }
}

繰り返しになりますが、これら2つの定義の補完的な性質がわかります。彼らは事実上反対です!これにより、オブジェクトとデータ構造の間の基本的な二分法が明らかになります。

手続き型コード(データ構造を使用するコード)を使用すると、既存のデータ構造を変更せずに新しい関数を簡単に追加できます。一方、オブジェクト指向コードを使用すると、既存の関数を変更せずに新しいクラスを簡単に追加できます。

補完も当てはまります。

手続き型コードでは、すべての関数を変更する必要があるため、新しいデータ構造を追加するのが難しくなります。オブジェクト指向コードでは、すべてのクラスを変更する必要があるため、新しい関数を追加するのが難しくなります。

成熟したプログラマーは、すべてがオブジェクトであるという考えが神話であることを知っています。時には、プロシージャが動作する単純なデータ構造が本当に必要な場合があります。

デメテルの法則

There is a well-known heuristic called the Law of Demeter that says a module should not know about the innards of the objects it manipulates.

More precisely, the Law of Demeter says that a method of a class should only call the methods of these:

f
C

  • C
  • An object created by
    f
  • An object passed as an argument to
    f
  • An object held in an instance variable of
    C

The method should not invoke methods on objects that are returned by any of the allowed functions. In other words, talk to friends, not to strangers.

Data Transfer Objects

The quintessential form of a data structure is a class with public variables and no functions. This is sometimes called a data transfer object, or DTO. DTOs are very useful structures, especially when communicating with databases or parsing messages from sockets, and so on. They often become the first in a series of translation stages that convert raw data in a database into objects in the application code.

Chapter 7 - Error Handling

Many code bases are completely dominated by error handling. When I say dominated, I don't mean that error handling is all that they do. I mean that it is nearly impossible to see what the code does because of all of the scattered error handling. Error handling is important, but if it obscures logic, it's wrong.

Use Exceptions Rather Than Return Codes

Back in the distant past there were many languages that didn't have exceptions. In those languages the techniques for handling and reporting errors were limited. You either set an error flag or returned an error code that the caller could check

Write Your Try-Catch-Finally Statement First

In a way, try blocks are like transactions. Your catch has to leave your program in a consistent state, no matter what happens in the try. For this reason it is good practice to start with a try-catch-finally statement when you are writing code that could throw exceptions. This helps you define what the user of that code should expect, no matter what goes wrong with the code that is executed in the try.

Provide Context with Exceptions

Each exception that you throw should provide enough context to determine the source and location of an error.

Create informative error messages and pass them along with your exceptions. Mention the operation that failed and the type of failure. If you are logging in your application, pass along enough information to be able to log the error in your catch.

Don't Return Null

If you are tempted to return null from a method, consider throwing an exception or returning a SPECIAL CASE object instead. If you are calling a null-returning method from a third-party API, consider wrapping that method with a method that either throws an exception or returns a special case object.

Don't Pass Null

Returning null from methods is bad, but passing null into methods is worse. Unless you are working with an API which expects you to pass null, you should avoid passing null in your code whenever possible.

Chapter 8 - Boundaries

We seldom control all the software in our systems. Sometimes we buy third-party pack- ages or use open source. Other times we depend on teams in our own company to produce components or subsystems for us. Somehow we must cleanly integrate this foreign code with our own.

Using Third-Party Code

There is a natural tension between the provider of an interface and the user of an interface. Providers of third-party packages and frameworks strive for broad applicability so they can work in many environments and appeal to a wide audience. Users, on the other hand, want an interface that is focused on their particular needs. This tension can cause problems at the boundaries of our systems. Example:

Map sensors = new HashMap();
Sensor s = (Sensor)sensors.get(sensorId);

VS

public class Sensors {
  private Map sensors = new HashMap();

  public Sensor getById(String id) {
    return (Sensor) sensors.get(id);
  }
  //snip
}

The first code exposes the casting in the Map, while the second is able to evolve with very little impact on the rest of the application. The casting and type management is handled inside the Sensors class.

The interface is also tailored and constrained to meet the needs of the application. It results in code that is easier to understand and harder to misuse. The Sensors class can enforce design and business rules.

Exploring and Learning Boundaries

Third-party code helps us get more functionality delivered in less time. Where do we start when we want to utilize some third-party package? It’s not our job to test the third-party code, but it may be in our best interest to write tests for the third-party code we use.

It's a good idea write some test for learn and understand how to use a third-party code. Newkirk calls such tests learning tests.

Learning Tests Are Better Than Free

The learning tests end up costing nothing. We had to learn the API anyway, and writing those tests was an easy and isolated way to get that knowledge. The learning tests were precise experiments that helped increase our understanding.

Not only are learning tests free, they have a positive return on investment. When there are new releases of the third-party package, we run the learning tests to see whether there are behavioral differences.

Using Code That Does Not Yet Exist

Some times it's necessary work in a module that will be connected to another module under develop, and we have no idea about how to send the information, because the API had not been designed yet. In this cases it's recommendable create an interface for encapsulate the communication with the pending module. In this way we maintain the control over our module and we can test although the second module isn't available yet.

Clean Boundaries

Interesting things happen at boundaries. Change is one of those things. Good software designs accommodate change without huge investments and rework. When we use code that is out of our control, special care must be taken to protect our investment and make sure future change is not too costly.

Chapter 9 - Unit Tests

Test Driven Development

The Three Laws of TDD

  • First Law You may not write production code until you have written a failing unit test.
  • Second Law You may not write more of a unit tests than is sufficient to fail, and not comipling is failing.
  • Third Law You may not write more production code than is sufficient to pass the currently failing tests.

Clean Tests

If you don't keep your tests clean, you will lose them.

The readability it's very important to keep clean your tests.

One Assert per test

It's recomendable maintain only one asserts per tests, because this helps to maintain each tests easy to understand.

Single concept per Test

This rule will help you to keep short functions.

  • Write one test per each concept that you need to verify

F.I.R.S.T

  • Fast Test should be fast.
  • Independient Test should not depend on each other.
  • Repeatable Test Should be repeatable in any environment.
  • Self-Validating Test should have a boolean output. either they pass or fail.
  • Timely Unit tests should be written just before the production code that makes them pass. If you write tests after the production code, then you may find the production code to be hard to test.

Chapter 10 - Classes

Class Organization

Encapsulation

We like to keep our variables and utility functions small, but we're not fanatic about it. Sometimes we need to make a variable or utility function protected so that it can be accessed by a test.

Classes Should be Small

  • First Rule: Classes should be small
  • Second Rule: Classes should be smaller than the first rule

The Single Responsibility Principle

Classes should have one responsibility - one reason to change

SRP is one of the more important concept in OO design. It's also one of the simple concepts to understand and adhere to.

Cohesion

Classes Should have a small number of instance variables. Each of the methods of a class should manipulate one or more of those variables. In general the more variables a method manipulates the more cohesive that method is to its class. A class in which each variable is used by each method is maximally cohesive.

Maintaining Cohesion Results in Many Small Classes

Just the act of breaking large functions into smaller functions causes a proliferation of classes.

Organizing for change

For most systems, change is continual. Every change subjects us to the risk that the remainder of the system no longer works as intended. In a clean system we organize our classes so as to reduce the risk of change.

Isolating from Change

Needs will change, therefore code will change. We learned in OO 101 that there are concrete classes, which contain implementation details (code), and abstract classes, which represent concepts only. A client class depending upon concrete details is at risk when those details change. We can introduce intefaces and abstract classes to help isolate the impact of those details.

Chapter 11 - Systems

Separe Constructing a System from using It

Software Systems should separate the startuo process, when the application objects are constructed and the dependencies are "wired" thogether, from the runtime logic that takes over after startup

Separation from main

One way to separate construction from use is simply to move all aspects of construction to , or modules called by , and to design the rest of the system assuming that all objects have been created constructed and wired up appropriately.

main
main

The Abstract Factory Pattern is an option for this kind of approach.

Dependency Injection

A powerful mechanism for separating construction from use is Dependency Injection (DI), the application of Inversion of control (IoC) to dependency management. Inversion of control moves secondary responsibilities from an object to other objects that are dedicated to the purpose, thereby supporting the Single Responsibility Principle. In context of dependency management, an object should not take responsibility for instantiating dependencies itself. Instead, it, should pass this responsibility to another "authoritative" mechanism, thereby inverting the control. Because setup is a global concern, this authoritative mechanism will usually be either the "main" routine or a special-purpose container.

Chapter 12 - Emergence

According to Kent Beck, a design is "simple" if it follows these rules

  • Run all tests
  • Contains no duplication
  • Expresses the intent of programmers
  • Minimizes the number of classes and methods

Chapter 13 - Concurrency

Concurrence is a decoupling strategy. It helps us decouple what gets fone from when it gets done. In single-threaded applications what and when are so strongly coupled that the state of the entire application can often be determined by looking at the stack backtrace. A programmer who debugs such a system can set a breakpoint, or a sequence of breakpoints, and know the state of the system by which breakpoints are hit.

Decoupling what from when can dramatically improve both the throughput and structures of an application. From a structural point of view the application looks like many lit- tle collaborating computers rather than one big main loop. This can make the system easier to understand and offers some powerful ways to separate concerns.

Miths and Misconceptions

  • Concurrency always improves performance. Concurrency can sometimes improve performance, but only when there is a lot of wait time that can be shared between multiple threads or multiple processors. Neither situ- ation is trivial.
  • Design does not change when writing concurrent programs. In fact, the design of a concurrent algorithm can be remarkably different from the design of a single-threaded system. The decoupling of what from when usually has a huge effect on the structure of the system.
  • Understanding concurrency issues is not important when working with a container such as a Web or EJB container. In fact, you’d better know just what your container is doing and how to guard against the issues of concurrent update and deadlock described later in this chapter. Here are a few more balanced sound bites regarding writing concurrent software:
  • Concurrency incurs some overhead, both in performance as well as writing additional code.
  • Correct concurrency is complex, even for simple problems.
  • Concurrency bugs aren’t usually repeatable, so they are often ignored as one-offs instead of the true defects they are.
  • Concurrency often requires a fundamental change in design strategy.

Chapter 14 - Successive Refinement

This chapter is a study case. It's recommendable to completely read it to understand more.

Chapter 15 - JUnit Internals

This chapter analize the JUnit tool. It's recommendable to completely read it to understand more.

Chapter 16 - Refactoring SerialDate

This chapter is a study case. It's recommendable to completely read it to understand more.

Chapter 17 - Smells and Heuristics

A reference of code smells from Martin Fowler's Refactoring and Robert C Martin's Clean Code.

While clean code comes from discipline and not a list or value system, here is a starting point.

Comments

C1: Inappropriate Information

Reserve comments for technical notes referring to code and design.

C2: Obsolete Comment

Update or delete obsolete comments.

C3: Redundant Comment

A redundant comment describes something able to sufficiently describe itself.

C4: Poorly Written Comment

Comments should be brief, concise, correctly spelled.

C5: Commented-Out Code

Ghost code. Delete it.

Environment

E1: Build Requires More Than One Step

Builds should require one command to check out and one command to run.

E2: Tests Require More Than One Step

Tests should be run with one button click through an IDE, or else with one command.

Functions

F1: Too Many Arguments

Functions should have no arguments, then one, then two, then three. No more than three.

F2: Output Arguments

Arguments are inputs, not outputs. If somethings state must be changed, let it be the state of the called object.

F3: Flag Arguments

Eliminate boolean arguments.

F4: Dead Function

Discard uncalled methods. This is dead code.

General

G1: Multiple Languages in One Source File

Minimize the number of languages in a source file. Ideally, only one.

G2: Obvious Behavior is Unimplemented

The result of a function or class should not be a surprise.

G3: Incorrect Behavior at the Boundaries

Write tests for every boundary condition.

G4: Overridden Safeties

Overriding safeties and exerting manual control leads to code melt down.

G5: Duplication

Practice abstraction on duplicate code. Replace repetitive functions with polymorphism.

G6: Code at Wrong Level of Abstraction

Make sure abstracted code is separated into different containers.

G7: Base Classes Depending on Their Derivatives

Practice modularity.

G8: Too Much Information

Do a lot with a little. Limit the amount of things going on in a class or functions.

G9: Dead Code

Delete unexecuted code.

G10: Vertical Separation

Define variables and functions close to where they are called.

G11: Inconsistency

Choose a convention, and follow it. Remember no surprises.

G12: Clutter

Dead code.

G13: Artificial Coupling

Favor code that is clear, rather than convenient. Do not group code that favors mental mapping over clearness.

G14: Feature Envy

Methods of one class should not be interested with the methods of another class.

G15: Selector Arguments

Do not flaunt false arguments at the end of functions.

G16: Obscured Intent

Code should not be magic or obscure.

G17: Misplaced Responsibility

Use clear function name as waypoints for where to place your code.

G18: Inappropriate Static

Make your functions nonstatic.

G19: Use Explanatory Variables

Make explanatory variables, and lots of them.

G20: Function Names Should Say What They Do

...

G21: Understand the Algorithm

Understand how a function works. Passing tests is not enough. Refactoring a function can lead to a better understanding of it.

G22: Make Logical Dependencies Physical

Understand what your code is doing.

G23: Prefer Polymorphism to If/Else or Switch/Case

Avoid the brute force of switch/case.

G24: Follow Standard Conventions

It doesn't matter what your teams convention is. Just that you have on and everyone follows it.

G25: Replace Magic Numbers with Named Constants

Stop spelling out numbers.

G26: Be Precise

Don't be lazy. Think of possible results, then cover and test them.

G27: Structure Over Convention

Design decisions should have a structure rather than a dogma.

G28: Encapsulate Conditionals

Make your conditionals more precise.

G29: Avoid Negative Conditionals

Negative conditionals take more brain power to understand than a positive.

G31: Hidden Temporal Couplings

Use arguents that make temporal coupling explicit.

G32: Don’t Be Arbitrary

Your code's sturcture should communicate the reason for its structure.

G33: Encapsulate Boundary Conditions

Avoid leaking +1's and -1's into your code.

G34: Functions Should Descend Only One Level of Abstraction

The toughest heuristic to follow. One level of abstraction below the function's described operation can help clarify your code.

G35: Keep Configurable Data at High Levels

High level constants are easy to change.

G36: Avoid Transitive Navigation

Write shy code. Modules should only know about their neighbors, not their neighbor's neighbors.

Names

N1: Choose Descriptive Names

Choose names that are descriptive and relevant.

N2: Choose Names at the Appropriate Level of Abstraction

Think of names that are still clear to the user when used in different programs.

N3: Use Standard Nomenclature Where Possible

Use names that express their task.

N4: Unambiguous Names

Favor clearness over curtness. A long, expressive name is better than a short, dull one.

N5: Use Long Names for Long Scopes

A name's length should relate to its scope.

N6: Avoid Encodings

No not encode names with type or scope information.

N7: Names Should Describe Side-Effects

Consider the side-effects of your function, and include that in its name.

Tests

T1: Insufficient Tests

Test everything that can possibly break

T2: Use a Coverage Tool!

Use your IDE as a coverage tool.

T3: 些細なテストをスキップしない

...

T4:無視されたテストはあいまいさについての質問です

テストが無視されると、コードが問題になります。

T5: 境界条件のテスト

真ん中は通常覆われています。境界を覚えておいてください。

T6: バグの近くを徹底的にテストする

バグが単独で発生することはめったにありません。見つけたら、近くで別のものを探します。

T7:失敗のパターンが明らかになっている

よく注文されたテストケースは、ファイアのパターンを明らかにします。

T8:テストカバレッジパターンが明らかになる可能性がある

同様に、失敗で渡されたコードと渡されないコードを見てください。

T9: テストは高速である必要があります

遅いテストは実行されません。