前七章の内容は紙の本を通じて読まれ、後の内容は以下の電子書籍リンクから得られました:
HOW DO YOU WRITE FUNCTIONS LIKE THIS? どうやってこのような関数を書くのか#
ソフトウェアを書くことは、他の種類の執筆と同じです。論文や記事を書くとき、まずは考えをまとめ、その後、読みやすくなるまで手を加えます。初稿は不器用で整理されていないかもしれませんが、言葉を練り直し、再構成し、洗練させて、望むように読みやすくします。
コードを書くことは他のものを書くことと非常に似ています。論文や記事を書くとき、まず考えたことを書き出し、その後それを磨きます。初稿はおそらく不格好で無秩序であり、考慮しながら推敲し、心に描く形に仕上げます。
私が関数を書くと、それは長くて複雑になります。インデントやネストされたループが多く、引数リストも長いです。名前は任意で、重複したコードもあります。しかし、私はその不器用なコードのすべてをカバーする単体テストのスイートも持っています。
私が関数を書くと、最初は冗長で複雑になります。インデントやネストされたループが多すぎ、引数リストも長すぎます。名前は任意で、重複したコードもあります。しかし、私はそのすべての不格好なコードをカバーする単体テストのスイートを持っています。
それから、私はそのコードを磨き、関数を分割し、名前を変更し、重複を排除します。メソッドを縮小し、順序を入れ替えます。時には全体のクラスを分割し、テストが通るように保ちます。
それから、私はそのコードを磨き、関数を分割し、名前を変更し、重複を排除します。メソッドを縮小し、順序を入れ替えます。時には全体のクラスを分割し、テストが通るように保ちます。最後に、この章に記載されているルールに従って、これらの関数を組み立てます。
最終的に、私はこの章で定めたルールに従った関数を得ます。最初からそのように書くわけではありません。誰もがそうできるとは思いません。
私は最初からルールに従って関数を書くわけではありません。誰もがそうできるとは思いません。
- プログラミングの芸術は、常に言語設計の芸術です。
- コメントの適切な使用は、コードで意図を表現する際の失敗を補うものです。注意してください、「失敗」という言葉を使いました。私は本気です。コメントは常に失敗です。私たちはコメントなしで自分を表現する方法を見つけることができないので、常にコメントが必要であり、これは祝うべきことではありません。
- Ps. だから、書かなければならないのです。今はすべての「失敗」を避けることができません。
- なぜ私はコメントを極力軽視しようとするのか?コメントは嘘をつくからです。常にそうであるわけではありませんが、あまりにも頻繁に発生します。コメントが存在する時間が長くなるほど、それが記述しているコードから遠ざかり、完全に間違ったものになります。理由は簡単です。プログラマーはコメントを維持することができません。
再び、私たちはこれら二つの定義の補完的な性質を見ます;それらは実質的に反対です!これはオブジェクトとデータ構造の間の根本的な二分法を明らかにします:
私たちは再びこの二つの定義の本質を見ます;それらは完全に対立しています。これはオブジェクトとデータ構造の間の二分原理を示しています:
手続き型コード(データ構造を使用するコード)は、既存のデータ構造を変更せずに新しい関数を追加するのが容易です。一方、OO コードは、既存の関数を変更せずに新しいクラスを追加するのが容易です。
手続き型コード(データ構造を使用するコード)は、既存のデータ構造を変更せずに新しい関数を追加するのが容易です。OO コードは、既存の関数を変更せずに新しいクラスを追加するのが容易です。
補完的なことも真実です:
逆に言えば、手続き型コードは新しいデータ構造を追加するのが難しいです。なぜなら、すべての関数を変更しなければならないからです。OO コードは新しい関数を追加するのが難しいです。なぜなら、すべてのクラスを変更しなければならないからです。
したがって、OO にとって難しいことは手続きにとっては簡単であり、手続きにとって難しいことは OO にとっては簡単です!
あらゆる複雑なシステムには、新しい関数を追加するのではなく、新しいデータ型を追加したいときがあります。これらのケースでは、オブジェクトと OO が最も適しています。一方で、新しいデータ型ではなく新しい関数を追加したいときもあります。その場合、手続き型コードとデータ構造がより適切です。
あらゆる複雑なシステムには、新しいデータ型を追加する必要があるときがあります。この場合、オブジェクトと OO が最も適しています。一方で、新しい関数を追加する必要があるときもあります。その場合、手続き型コードとデータ構造がより適切です。
成熟したプログラマーは、すべてがオブジェクトであるという考えが神話であることを知っています。時には、本当に手続きが動作するシンプルなデータ構造が必要です。
私たちが ACMEPort のために定義したようなラッパーは非常に便利です。実際、サードパーティ API をラップすることはベストプラクティスです。サードパーティ API をラップすると、それへの依存を最小限に抑えることができます:将来的に他のライブラリに移行する際のペナルティが少なくなります。ラッピングは、独自のコードをテストするときにサードパーティの呼び出しをモックするのを容易にします。
ACMEPort のために定義したようなラッパーは非常に便利です。実際、サードパーティ API をラップすることは良いプラクティスです。サードパーティ API をラップすると、それへの依存を最小限に抑えることができます:将来的に他のライブラリに移行する際のペナルティが少なくなります。ラッピングは、独自のコードをテストするときにサードパーティの呼び出しをモックするのを容易にします。
ラッピングのもう一つの利点は、特定のベンダーの API 設計の選択に縛られないことです。自分が快適に感じる API を定義できます。前の例では、ポートデバイスの障害のために単一の例外タイプを定義し、はるかにクリーンなコードを書くことができることがわかりました。
ラッピングの利点は、特定のベンダーの API 設計に縛られないことです。自分が快適に感じる API を定義できます。前の例では、ポートデバイスの障害のために単一の例外タイプを定義し、はるかにクリーンなコードを書くことができることがわかりました。
特定のコード領域に対して、単一の例外クラスが適していることがよくあります。例外とともに送信される情報がエラーを区別できます。異なるクラスを使用するのは、特定の例外をキャッチし、他の例外を通過させたい場合だけです。
特定のコード領域に対して、単一の例外クラスが適していることがよくあります。例外とともに送信される情報がエラーを区別できます。特定の例外をキャッチし、他の例外を通過させたい場合だけ、異なる例外クラスを使用します。
これまでの内容は主に紙の本を通じて読まれたもので、ノートを整理するのが難しかった(実際には怠けていた)ため、上に散発的に置かれています。次の数章は主に大御所のオンライン翻訳(上部リンク参照)を通じて読まれ、記録がしやすかったです。
第八章 境界#
- 私たちにはサードパーティのコードをテストする責任はありませんが、使用するサードパーティのコードに対してテストを書くことは、私たちの利益に最も適しているかもしれません。
サードパーティのコードを学ぶことは難しいです。サードパーティのコードを統合することも難しいです。同時に両方を行うことは二重に難しいです。異なるアプローチを取ってみたらどうでしょうか?新しいものを本番コードで試すのではなく、サードパーティのコードを理解するためのテストを書くことができます。ジム・ニューカークはそのようなテストを学習テストと呼んでいます。
サードパーティのコードを学ぶことは難しいです。サードパーティのコードを統合することも難しいです。同時に両方を行うことは二重に難しいです。異なるアプローチを取ってみたらどうでしょうか?新しいものを本番コードで試すのではなく、サードパーティのコードを理解するためのテストを書くことができます。ジム・ニューカークはそのようなテストを学習テストと呼んでいます。
学習テストでは、私たちはアプリケーションで使用することを期待してサードパーティ API を呼び出します。私たちは基本的に、その API に対する理解を確認するための制御された実験を行っています。テストは、API から何を望んでいるかに焦点を当てています。
学習テストでは、私たちはアプリケーションで使用することを期待してサードパーティ API を呼び出します。私たちは基本的に、その API に対する理解を確認するための制御された実験を行っています。テストは、API から何を望んでいるかに焦点を当てています。
第九章 単体テスト#
この決定に共感する読者もいるかもしれません。おそらく、長い昔に、あなたも私がその Timer クラスのために書いたようなテストを書いたことがあるでしょう。そのような使い捨てのテストを書くことから、自動化された単体テストのスイートを書くことへの大きなステップです。したがって、私が指導していたチームのように、汚いテストを持つことがテストがないよりも良いと考えるかもしれません。
この決定に共感する読者もいるかもしれません。おそらく、長い昔に、あなたも私がその Timer クラスのために書いたようなテストを書いたことがあるでしょう。そのような使い捨てのテストを書くことから、自動化された単体テストのスイートを書くことへの大きなステップです。したがって、私が指導していたチームのように、汚いテストを持つことがテストがないよりも良いと考えるかもしれません。
このチームが理解していなかったのは、汚いテストを持つことは、テストがないことと同等、あるいはそれ以上に悪いということです。問題は、テストは本番コードが進化するにつれて変更しなければならないということです。テストが汚いほど、変更が難しくなります。テストコードが絡み合うほど、新しいテストをスイートに詰め込むのにかかる時間が、新しい本番コードを書くのにかかる時間よりも長くなる可能性が高くなります。本番コードを修正すると、古いテストが失敗し始め、テストコードの混乱がそれらのテストを再度通過させるのを難しくします。したがって、テストはますます増大する負債と見なされるようになります。
このチームが理解していなかったのは、汚いテストを持つことは、テストがないことと同等、あるいはそれ以上に悪いということです。問題は、テストは本番コードが進化するにつれて変更しなければならないということです。テストが汚いほど、変更が難しくなります。テストコードが絡み合うほど、新しいテストをスイートに詰め込むのにかかる時間が、新しい本番コードを書くのにかかる時間よりも長くなる可能性が高くなります。本番コードを修正すると、古いテストが失敗し始め、テストコードの混乱がそれらのテストを再度通過させるのを難しくします。したがって、テストはますます増大する負債と見なされるようになります。
9.3 - 整然としたテスト#
BUILD-OPERATE-CHECK2 パターンは、これらのテストの構造によって明らかです。各テストは明確に三つの部分に分かれています。最初の部分はテストデータを構築し、二番目の部分はそのテストデータに対して操作を行い、三番目の部分はその操作が期待される結果をもたらしたかを確認します。
これらのテストは明らかに構築 - 操作 - 検証(BUILD-OPERATE-CHECK)パターンを示しています。各テストは明確に三つの部分に分かれています。最初の部分はテストデータを構築し、二番目の部分はそのテストデータに対して操作を行い、三番目の部分はその操作が期待される結果をもたらしたかを確認します。
9.5-F.I.R.S.T#
タイムリー テストはタイムリーに書かれる必要があります。単体テストは、それを通過させる本番コードの直前に書かれるべきです。本番コードの後にテストを書くと、本番コードがテストしにくくなることがあります。ある本番コードがテストしにくいと判断するかもしれません。本番コードをテスト可能に設計しないかもしれません。
タイムリー(Timely)テストはタイムリーに書かれる必要があります。単体テストは、それを通過させる本番コードの直前に書かれるべきです。本番コードの後にテストを書くと、本番コードがテストしにくくなることがあります。ある本番コードがテストしにくいと判断するかもしれません。本番コードをテスト可能に設計しないかもしれません。
第十章 - クラス#
この章では、私はそのほとんどの概念を Go の interface {} に移行して理解しようとしました。
クラスの名前は、その責任を説明するべきです。実際、命名はクラスのサイズを判断するための最初の方法です。クラスに簡潔な名前を付けられない場合、そのクラスはおそらく大きすぎます。クラス名があいまいであればあるほど、そのクラスは過剰な責任を持つ可能性が高くなります。たとえば、Processor や Manager、Super のような曖昧な言葉を含むクラス名は、しばしば不幸な責任の集約を示唆します。
クラスの名前は、その責任を説明するべきです。実際、命名はクラスのサイズを判断するための最初の方法です。クラスに簡潔な名前を付けられない場合、そのクラスはおそらく大きすぎます。クラス名があいまいであればあるほど、そのクラスは過剰な責任を持つ可能性が高くなります。たとえば、Processor や Manager、Super のような曖昧な言葉を含むクラス名は、しばしば不幸な責任の集約を示唆します。
内聚#
- クラスが内聚性を失ったときは、それを分割せよ!
したがって、大きな関数を多くの小さな関数に分割することは、いくつかの小さなクラスを分割する機会を与えてくれます。これにより、プログラムははるかに良い組織と透明な構造を持つことができます。
したがって、大きな関数を多くの小さな関数に分割することは、いくつかの小さなクラスを分割する機会を与えてくれます。これにより、プログラムははるかに良い組織と透明な構造を持つことができます。
10.3 - 変更のために組織する#
システムがこのようにテストできるほど解耦されている場合、それはより柔軟で再利用を促進します。結合がないということは、システムの要素が互いに、また変化からもより良く隔離されていることを意味します。この隔離により、システムの各要素を理解しやすくなります。
システムがこのようにテストできるほど解耦されている場合、それはより柔軟で再利用を促進します。結合がないということは、システムの要素が互いに、また変化からもより良く隔離されていることを意味します。この隔離により、システムの各要素を理解しやすくなります。
このように結合を最小限に抑えることで、私たちのクラスは依存関係逆転の原則(DIP)という別のクラス設計原則に従います。要するに、DIP は私たちのクラスが具体的な詳細ではなく、抽象に依存すべきだと言っています。
このように結合を最小限に抑えることで、私たちのクラスは依存関係逆転の原則(DIP)という別のクラス設計原則に従います。要するに、DIP は私たちのクラスが具体的な詳細ではなく、抽象に依存すべきだと言っています。
第 11 章 - システム#
「最初からシステムを正しく作ることができる」というのは神話です。むしろ、私たちは今日のストーリーだけを実装し、その後、システムをリファクタリングして明日新しいストーリーを実装するべきです。これが反復的かつ増分的なアジリティの本質です。テスト駆動開発、リファクタリング、そしてそれらが生み出すクリーンコードは、コードレベルでこれを実現します。
「最初からシステムを正しく作ることができる」というのは神話です。むしろ、私たちは今日のストーリーだけを実装し、その後、システムをリファクタリングして明日新しいストーリーを実装するべきです。これが反復的かつ増分的なアジリティの本質です。テスト駆動開発、リファクタリング、そしてそれらが生み出すクリーンコードは、コードレベルでこれを実現します。
早すぎる手を出しは禁物
私たちは、最も資格のある人に責任を与えるのが最善であることを皆知っています。私たちはしばしば、決定を最後の瞬間まで延期するのが最善であることを忘れます。これは怠惰や無責任ではありません;それは私たちが最良の情報に基づいて情報に基づいた選択をすることを可能にします。早すぎる決定は、最適でない知識に基づく決定です。早すぎる決定を下すと、顧客からのフィードバック、プロジェクトに対する精神的な反省、実装の選択に関する経験が大幅に減少します。
私たちは、最も資格のある人に責任を与えるのが最善であることを皆知っています。私たちはしばしば、決定を最後の瞬間まで延期するのが最善であることを忘れます。これは怠惰や無責任ではありません;それは私たちが最良の情報に基づいて情報に基づいた選択をすることを可能にします。早すぎる決定は、最適でない知識に基づく決定です。早すぎる決定を下すと、顧客からのフィードバック、プロジェクトに対する精神的な反省、実装の選択に関する経験が大幅に減少します。
システムや個々のモジュールを設計する際には、必ず機能する可能性のある最もシンプルなものを使用することを忘れないでください。
システムや個々のモジュールを設計する際には、必ず機能する可能性のある最もシンプルなものを使用することを忘れないでください。
第 12 章 Emergence 進化#
ケントによれば、デザインは「シンプル」である場合、以下のルールに従います:
ケントによれば、デザインは「シンプル」である場合、以下のルールに従います:
- すべてのテストを実行する
- 重複がない
- プログラマーの意図を表現する
- クラスとメソッドの数を最小限に抑える
すべてのテストを実行する;重複がない;プログラマーの意図を表現する;クラスとメソッドの数を最小限に抑える;
ルールは重要度の順に示されています。
ルールは重要度の順に示されています。
驚くべきことに、テストを持ち、継続的に実行する必要があるというシンプルで明白なルールに従うことは、システムの低結合性と高内聚性という主要な OO 目標への遵守に影響を与えます。テストを書くことは、より良いデザインにつながります。
驚くべきことに、テストを持ち、継続的に実行する必要があるというシンプルで明白なルールに従うことは、システムの低結合性と高内聚性という主要な OO 目標への遵守に影響を与えます。テストを書くことは、より良いデザインにつながります。
このリファクタリングのステップでは、良いソフトウェアデザインに関する全体の知識を適用できます。内聚性を高め、結合を減らし、関心を分離し、システムの関心をモジュール化し、関数やクラスを縮小し、より良い名前を選ぶなどです。これは、シンプルなデザインの最後の三つのルールを適用する場所でもあります:重複を排除し、表現力を確保し、クラスとメソッドの数を最小限に抑えることです。
このリファクタリングのステップでは、良いソフトウェアデザインに関する全体の知識を適用できます。内聚性を高め、結合を減らし、関心を分離し、システムの関心をモジュール化し、関数やクラスを縮小し、より良い名前を選ぶなどです。これは、シンプルなデザインの最後の三つのルールを適用する場所でもあります:重複を排除し、表現力を確保し、クラスとメソッドの数を最小限に抑えることです。
私たちの多くは、複雑なコードに取り組んだ経験があります。私たちの多くは、自分自身で複雑なコードを生成したことがあります。自分が理解できるコードを書くのは簡単です。なぜなら、私たちがそれを書くとき、私たちは解決しようとしている問題を深く理解しているからです。コードの他のメンテナは、そこまで深く理解しているわけではありません。
私たちの多くは、複雑なコードに取り組んだ経験があります。私たちの多くは、自分自身で複雑なコードを生成したことがあります。自分が理解できるコードを書くのは簡単です。なぜなら、私たちがそれを書くとき、私たちは解決しようとしている問題を深く理解しているからです。コードの他のメンテナは、そこまで深く理解しているわけではありません。
したがって、ソースコードを読む一つのアプローチは、テストから始めることです(もちろん、読む対象が十分に優れたテストコードを持ち、カバレッジが高いことが前提です)。
良く書かれた単体テストは、表現力も持っています。テストの主な目的の一つは、例を通じてドキュメントとして機能することです。私たちのテストを読む人は、クラスが何をするものかをすぐに理解できるはずです。
良く書かれた単体テストは、表現力も持っています。テストの主な目的の一つは、例を通じてドキュメントとして機能することです。私たちのテストを読む人は、クラスが何をするものかをすぐに理解できるはずです。
第 13 章 Concurrency 並行プログラミング#
「オブジェクトは処理の抽象です。スレッドはスケジュールの抽象です。」
— ジェームズ・O・コプリエン
「オブジェクトは処理の抽象です。スレッドはスケジュールの抽象です。」—— ジェームズ・O・コプリエン
ここに、並行ソフトウェアを書くことに関するいくつかのバランスの取れた発言があります:
ここに、並行ソフトウェアを書くことに関するいくつかのバランスの取れた発言があります:
- 並行性は、パフォーマンスや追加のコードを書くことにおいていくつかのオーバーヘッドを伴います。
- 正しい並行性は複雑であり、単純な問題でもそうです。
- 並行性のバグは通常再現性がなく、真の欠陥としてではなく、偶発的なものとして無視されることが多いです。
- 並行性はしばしば設計戦略の根本的な変更を必要とします。
並行性は、パフォーマンスや追加のコードを書くことにおいていくつかのオーバーヘッドを伴います;正しい並行性は複雑であり、単純な問題でもそうです;並行性のバグは通常再現性がなく、真の欠陥としてではなく、偶発的なものとして無視されることが多いです;並行性はしばしば設計戦略の根本的な変更を必要とします。
- 並行性に関連するコードには、独自の開発、変更、調整のライフサイクルがあります。
- 並行性に関連するコードには、非並行性のコードとは異なり、しばしばより困難な独自の課題があります。
- 誤って書かれた並行性ベースのコードが失敗する方法の数は、周囲のアプリケーションコードの追加の負担なしでも十分に挑戦的です。
並行性に関連するコードには、独自の開発、変更、調整のライフサイクルがあります;並行性に関連するコードには、非並行性のコードとは異なり、しばしばより困難な独自の課題があります;誤って書かれた並行性ベースのコードが失敗する方法の数は、周囲のアプリケーションコードの追加の負担なしでも十分に挑戦的です。
推奨:並行性に関連するコードを他のコードから分離してください。
推奨:並行性に関連するコードを他のコードから分離してください。
第 14 章 Successive Refinement 漸進的改善#
あなたの心を休めましょう。私はこのプログラムを最初から最後まで現在の形で書いたわけではありません。もっと重要なのは、あなたが一度でクリーンでエレガントなプログラムを書くことができるとは期待していないということです。過去数十年で学んだことがあるとすれば、それはプログラミングが科学よりも技術であるということです。クリーンなコードを書くためには、まず汚いコードを書き、それをクリーンにする必要があります。
あなたの心を休めましょう。私はこのプログラムを最初から最後まで現在の形で書いたわけではありません。もっと重要なのは、あなたが一度でクリーンでエレガントなプログラムを書くことができるとは期待していないということです。過去数十年で学んだことがあるとすれば、それはプログラミングが科学よりも技術であるということです。クリーンなコードを書くためには、まず汚いコードを書き、それをクリーンにする必要があります。
漸進主義は、他の変更を加える前にこれを迅速に動作させることを要求しました。実際、修正はそれほど難しくありませんでした。私は単に null のチェックを移動する必要がありました。もはや boolean が null であるかどうかをチェックする必要はなく、ArgumentMarshaller をチェックする必要がありました。
漸進主義は、他の変更を加える前にこれを迅速に動作させることを要求しました。実際、修正はそれほど難しくありませんでした。私は単に null のチェックを移動する必要がありました。もはや boolean が null であるかどうかをチェックする必要はなく、ArgumentMarshaller をチェックする必要がありました。
14、15、16 章は基本的に実践的な内容ですが、この本の例は Java に基づいているため、私は Java にあまり詳しくない(ケースは必然的に Java の特有の特徴やライブラリに関わる)ので、これらの三章にあまり時間をかけないことにしました。直接最後のまとめの章に焦点を戻しました。
第 17 章 Smells and Heuristics 匂いとヒューリスティックス#
この章の内容は、以前の内容の要約に相当するため、英語の原文を一緒に引用することは控えます。また、全文が完全であることは保証されません。結局、これは私のノートの一部です。一般的には、私が感銘を受けた部分だけを記録します。
- 無関係または不正確なコメントは廃棄されたコメントです。コメントはすぐに時代遅れになります。廃棄されるコメントを書くのは避けるべきです。廃棄されたコメントを見つけたら、できるだけ早く更新または削除するのが最善です。廃棄されたコメントは、かつて記述していたコードから遠ざかり、無関係で誤解を招く浮島となります。
- コメントが自己説明的なものである場合、コメントは冗長です。
下記の G シリーズは 17.4 一般的な問題に由来します。
G2:明らかな動作が実装されていない#
-
「最小驚異の原則」(The Principle of Least Surprise)に従い、関数やクラスは他のプログラマーが期待する動作を実装すべきです。たとえば、日付名をその日付を表す列挙型に変換する関数を考えてみてください。
Day day = DayDate.StringToDay(String dayName);
-
明らかな動作が実装されていない場合、読者やユーザーは関数名に対する直感に依存できなくなります。彼らは元の作者を信頼せず、コードの詳細を読む必要があります。
G3:不正確な境界動作#
- 注意深さに代わるものはありません。すべての境界条件、すべての極端な状況、すべての例外は、優雅で明白なアルゴリズムを混乱させる可能性のあるものを表します。直感に依存しないでください。すべての境界条件を追跡し、テストを作成してください。
G5:重複#
- 重複したコードを見るたびに、抽象が欠落していることを示しています。重複したコードはサブルーチンや別のクラスになる可能性があります。重複したコードを類似の抽象に重ねることで、設計言語の語彙を増やすことができます。他のプログラマーは、あなたが作成した抽象的な施設を利用できます。コーディングがより迅速になり、エラーが減少します。抽象レベルを向上させるからです。
- より隠れた形態は、類似のアルゴリズムを持つが具体的なコード行が異なるモジュールです。これも重複であり、テンプレートメソッドパターンやストラテジーパターンを使用して修正できます。
G6:間違った抽象レベルのコード#
- 良いソフトウェアデザインは、異なるレベルにある概念を分離し、それらを異なるコンテナに配置することを要求します。これらのコンテナは、基底クラスや派生クラスであることもあれば、ソースファイル、モジュール、コンポーネントであることもあります。どちらの場合でも、分離は完全であるべきです。低レベルの概念と高レベルの概念は混在してはいけません。
G10: 垂直分離#
- 変数と関数は、使用される場所の近くで定義されるべきです。ローカル変数は、その最初の使用位置のすぐ上に宣言されるべきであり、垂直距離は短くするべきです。ローカル変数は、その使用位置から数百行離れた場所で宣言されるべきではありません。
- プライベート関数は、その最初の使用位置のすぐ下に定義されるべきです。プライベート関数はクラス全体に属しますが、呼び出しと定義の間の垂直距離を制限する必要があります。プライベート関数を見つけるのは、その最初の使用位置から少し下を見るだけで済むはずです。
G11:前後不一致#
- 特定の関数で response という名前の変数を HttpServletResponse オブジェクトを保持するために使用する場合、他の HttpServletResponse オブジェクトを使用する関数でも同じ変数名を使用するべきです。processVerificationRequest というメソッドを名付けた場合、他のリクエストタイプを処理するメソッドにも似た名前を付けるべきです。たとえば、processDeletionRequest などです。
G17:位置が間違った責任#
- 最小驚異の原則がここで機能します。コードは、読者が自然に期待する場所に配置されるべきです。PI 定数は、三角関数を宣言する場所に配置されるべきです。OVERTIME_RATE 定数は、HourlyPayCalculator クラスに宣言されるべきです。
G20:関数名はその動作を表現すべきです#
- 関数の実装(またはドキュメント)を見なければその関数が何をするのかがわからない場合は、より良い関数名に変更するか、機能コードを再配置して、より良い名前の関数に移動すべきです。
G26:正確性#
- あるクエリの最初の一致が唯一の一致であることを期待するのは過度に楽観的です。浮動小数点数で通貨を表すことはほぼ犯罪です。並行更新を避けるためにロックやトランザクション管理を使用しないことは、良い意味での怠惰です。List を使用できる場合に、変数を ArrayList として宣言するのは過度に制約されています。すべての変数を protected に設定することは、十分な自己規律ではありません。
- コード内で決定を下す際には、正確であることを確認してください。なぜそうするのか、異常な状況にどのように対処するのかを明確にしてください。決定の正確性を無視しないでください。null を返す可能性のある関数を呼び出す予定がある場合、null 値を確認したことを確認してください。データベース内に唯一のレコードがあると考えている場合、他のレコードが存在しないことを確認してください。通貨データを処理する場合は整数を使用し、適切に四捨五入を処理してください。並行更新がある可能性がある場合、何らかのロックメカニズムを実装したことを確認してください。
- コード内の曖昧さや不正確さは、意見の相違の結果であるか、怠惰から生じます。理由が何であれ、それを排除してください。
G28:条件のカプセル化#
-
if や while 文の文脈がなければ、ブールロジックは理解しにくいです。条件の意図を説明する関数を抽出すべきです。
// 例えば: if (shouldBeDeleted(timer)) is preferable to // より良いのは if (timer.hasExpired() && !timer.isRecurrent())
G33:境界条件のカプセル化#
-
境界条件は追跡が難しいです。境界条件を処理するコードを一箇所に集中させ、コード中に散らばらないようにしてください。+1 や - 1 の文字列があちこちに見られることは望ましくありません。
if(level + 1 < tags.length) { parts = new Parse(body, tags, level + 1, offset + endTag); body = null; } // 注意、level + 1が二回出現しています。これはnextLevelのような変数にカプセル化すべき境界条件です。 int nextLevel = level + 1; if(nextLevel < tags.length) { parts = new Parse(body, tags, nextLevel, offset + endTag); body = null; }
17.6 名前#
N2:名前は抽象レベルに一致すべきです#
- 実装を伝える名前を取らないでください;クラスや関数の抽象レベルを反映する名前を取ってください。これを行うのは簡単ではありません。人々は抽象レベルを混同するのが得意です。コードを閲覧するたびに、変数の名前のレベルが低すぎることに気づくでしょう。その機会を利用して名前を変更してください。コードを可読にするためには、継続的な改善が必要です。以下の Modem インターフェースを見てください:
N5:広い範囲には長い名前を選ぶ#
- 名前の長さは、その範囲の広さに関連すべきです。小さな範囲には非常に短い名前を使用できますが、広い範囲には長い名前を使用するべきです。
- i や j のような変数名は、5 行以内の範囲には問題ありません。