# クリーンコード 第17章 ## 第17章 においと経験則 マーチン・ファウラーが明らかにした、「コードのにおい」は、リファクタリングを行う基準となる兆候として有名である。 ファウラーのにおいに加えて、筆者が独自に加えたものや経験則をリストの形式でまとめている。 ### Comment:コメント(第4章参照) #### C1:不適切な情報 別のシステムで代替できるような情報を、コメントに載せない。 #### C2:退化コメント 過去の情報が書かれ、現状と乖離しているコメント。直ちに最新化するか取り除く。 #### C3:冗長なコメント 見ればわかるコードに書いてあるコメントや、何行にもわたって余計な記述のあるコメント、Javadocコメント等。いずれも削除する。 #### C4:記述不足のコメント **コメントを書く以上は、要点を正確につかみ、適切な言葉で表さなければならない。** 記述不足や不要なコメントは書かない。 #### C5:コメントアウトされたコード 使っていない限りリソースの無駄遣いである。発見次第削除する。 ### Environment:環境 #### E1:ビルドに複数ステップを要する ビルドは、基本的に単一の実行で全てをビルドできる必要がある。 ビルドに他の成果物を必要とする状態は避ける。 #### E2:テストに複数ステップを要する テストも同様に、単一の実行で全てをテストできる必要がある。 ### Function:関数(第3章参照) #### F1:多すぎる引数 引数は0がベスト。使って1,または2個である。 3つ以上の場合、リファクタリングが必要。 #### F2:出力引数 引数をそのまま出力値に戻すような使い方は避ける。 型を引き継ぐ必要がある場合、関数内で同型の出力専用のものに置き換えること。 #### F3:フラグ引数(boolean型引数) boolean型引数は、使うだけでその関数が **「1つのことに1つのことを」の原則**を根本から破ることになる。使用しない。 #### F4:死んだ関数(デッドコード) 呼び出されないデッドコードが残っていたらすぐに消去! ### General:一般 #### G1:1つのソースファイルに複数言語を使用する HTMLとJava等、複数言語を記述することが可能である。 これらの記述はなるべく最小限度に抑える必要がある。 1つのソースファイルに言語は1つが理想! #### G2:あって当然の振る舞いが実装されていない 「**驚き最小の原則**」に従い、プログラマが当然と期待する振る舞いは、全てのクラスや関数で行われなければならない。 (例:曜日変化に伴う、英語表記、英語略称の変化) #### G3:境界値に対する不適切な振る舞い **境界値チェック**は、当然のように思えて意外と罠にハマりやすい。いわばバグの温床である。 直感で判断するのはやめて、あらゆる条件を網羅するテストを行う必要がある。 #### G4:安全軽視 **どんな時も安全を疎かにしてはならない。** 危険を伴うコード(serialVersionUID等)の扱いには細心の注意を払わなくてはならない。 #### G5:重複 **最も重要なルールの一つで、真剣に取り組む必要がある。** **抽象化を妨げる、あらゆる場所の重複は排除する!** #### G6:抽象レベルが不適切なコード 抽象化は、高いレベルの概念を抽象クラスに置き、その継承クラスに低いレベルの概念を置くことである。 原則として**両概念を混在させてはならない**。混在している場合、偽の情報や見せかけで取り繕うことは不可能である。 一度間違えると修正は困難になってしまう。 #### G7:継承クラスに依存したベースクラス ベースクラスが継承クラスの名前を知っている場合、両者には依存関係があると言える。 こうした**依存関係はなるべく排除**し、**独立したコンポーネント**として扱うべきである。 #### G8:情報過多 クラスに関数や変数などのメソッドが大量に存在する場合、モジュール結合度が高く多くの依存関係があると言える。 こうした**依存関係は排除して、フィールドやメソッドは可能な限りカプセル化を行うべきである。** #### G9:デッドコード 呼び出されない、使用されないデッドコードは、どんどん腐っていく。 デッドコードは、どんどん消してスッキリ爽快に! #### G10:垂直分離 宣言から利用されるまで大きく離れている状態。 ローカル変数の場合、**変数宣言は最初に利用される場所から最も近い上部に置かなければならない。** また、privateメソッドは、**呼び出される関数から最も近い下部におかなければならない。** 垂直分離の解消は、関数の抽出を行う前に必須のリファクタリングである。 #### G11:不整合 一度方法を決定したら、それに類する全てのものを同じ方法で実行しなければならない。 **1つの名前に1つの意味!以後、同じ意味は一貫して同じ名前を使用し続けること!** 同義名が複数あり、不整合が生じないようにするべきである。 #### G12:雑然 意味のない書き込み(デフォルトコンストラクタ、未使用変数など)は雑然の元!全部まとめて消去し、クリーンな状態にすること! #### G13:人為的な結合 依存関係が無いのに、2つのモジュールが関連し合っているような状態は避ける。 適当に便利だからと、一般用途の関数を特定のクラスに含めたり宣言したりするのは、もはや怠慢である。 #### G14:機能の羨望 ファウラーが提唱したにおいの一つで、**クラスの関数が、別のクラスの変数や関数に関心を持つべきではない**ことを意味する。 別のオブジェクトのアクセサやミューテータを用いてデータにアクセスしている場合、他オブジェクトの機能を羨望していると言える。 **他オブジェクトのメソッドをわざわざ呼び出すような書き方は避ける**。 但し、オブジェクトに与えたくない情報(書式設定など)を別クラスで定義したい場合は例外である。 #### G15:セレクタ引数 F3のフラグ引数とほぼ同様で、boolean型を引数にすると、2つの機能を同時に持たせてしまうことなる。 さらに、**関数の抽出によって関数を小さくする**という、リファクタリングで非常に大切な行為を妨げてしまう要因にもなる。 boolean型の引数はデメリットが多いので避ける! #### G16:不明瞭な意図 **コードは出来る限り表現豊かにする必要がある。** 冗長な式やハンガリアン記法、マジックナンバー等、意図がみえにくくなるような記述は避ける。 #### G17:責務を持たせる場所の間違い **関数をどこに配置して、責務をどこに持たせるかは、最も重要な判断の一つである。** G2の「驚き最小の原則」に倣い、読み手が当然と思う位置におくべきである。 適切な名前が、どこにどのような責務があるかを把握する判断基準となる。 #### G18:不適切なstatic staticは、特定のオブジェクトに依存することなく、かつ多態的に用いない場合に用いる。 しかし、本来staticでないのにstaticをつけてしまうケースもある。多態的な使い方をしたい場合は、インスタンスメソッドとして非staticとなる必要がある。 基本的に、非staticのインスタンスメソッドの方が好まれるため、staticをあまり多用しない方が良い。 #### G19:説明的変数 インライン化ができる状況で、一時的に変数を置いて変数の意味を説明している。 大きい関数の場合や、変数で置き換えている部分が短い場合はインライン化すべきである。 しかし、関数の大きさが小さく、さらに**意図が読み手に伝わるように説明的変数を置く**場合は、**読みやすさや透明性の改善の面で非常に良い効果を発揮する。** 後者のメリットを生かせるように活用すべきである。 #### G20:関数名は体を表す **関数名は、関数の振る舞いを明確に説明するものでなくてはならない。** 関数名の命名が不適切だと、読み手に誤解を招く恐れがある。 #### G21:アルゴリズムを理解する **アルゴリズムの理解不足**が、大量のおかしなコードを生んでしまう。 正しい振る舞いを行うためには、得られる解が正確であるかどうか確認する必要がある。 また、G20のように、関数名の適切な命名が不可欠である。 #### G22:論理的な依存性を物理的なものにする モジュールの依存関係は、論理的なものではなく物理的なものである。 したがって、**モジュールの呼び出し側が、呼び出されるモジュールに前提条件を付与する(論理的に依存する)べきではない。** G17のように責務の配置ミスを犯すと、論理的依存が生まれてしまう。 依存先のどの情報に依存しているのかを知り、依存性解消へと動くべきである。 #### G23:if/elseやswitch/caseよりも多態を好む 幾つにもわたる条件式は、冗長性の原因となる。 まずは**多態(ポリモーフィズム)を用いて、振る舞いの分割を検討すべきである。** それでも難しい場合は、1度だけswitch/case文を使用し、専用オブジェクトを多態的に作成して、以後そのオブジェクトを使用するべきである。 #### G24:標準の規約に従う 業界で一般的なコーディング標準に従うべきである。 チームの場合は、チームで決めたコード規約に従うべきである。 #### G25:マジックナンバーを、名前づけした定数に置き換える マジックナンバーとは、定数をそのまま数字としてコードに残したり、値の意味が不明確なコードを指す。 こうしたマジックナンバーは適切に名前づけされた定数で定義すべきである。 #### G26:正確であれ 最初に見つかったものが、唯一の解であることはまず無い。 複数の類似の表現方法から、「**正確なもの**」を選ばなければならない。 **なぜこれを使ったのかという意図をハッキリさせる必要がある。** #### G27:規約より構造 命名規約よりも構造による制約を意識して設計すべきである。 #### G28:条件をカプセル化せよ 冗長な条件式は、抽出してカプセル化を行うべし! #### G29:条件の否定形を避ける 肯定形の条件よりも分かりにくい否定形の条件は使わない! #### G30:1つの関数では1つのことを タイトル通り!複数責務を持たないようにする。 #### G31:隠れた時間軸上の結合 時間軸上の結合が生じる場合は、**呼び出す順序が明確になるよう**に記述すること。 関数を変数定義し、その変数を次の関数の引数として用いることで、順序が固定されるように記述するのも一つの手である。 #### G32:いい加減にならない コードを構成する時は、常に根拠を持たなければならない。 根拠と構造に矛盾が生じないように注意すること。 構造がいい加減だと、読み手はその構造を変更したいと思ってしまう。良い構造は読み手に問題なく構造を使わせることができる。 #### G33:境界条件のカプセル化 境界値を判断する条件がばらけていると扱いづらい。 関連する全ての条件を抽出してまとめ上げ、カプセル化! #### G34:関数は1つの抽象レベルを担うべき 1つの関数の中では、同一レベルの抽象度である必要がある。 **抽象レベルを分離することは、リファクタリングで最も重要な機能であると同時に、正しく行うことが最も難しい作業の一つでもある。** #### G35:設定可能なデータは高いレベルに置く 高レベル(抽象クラス)内で定義される定数やデフォルト値を、低レベル(具象クラス)内に持ち込まないこと。 持ち込みが必要な場合は呼び出しの引数で渡す。 #### G36:推移的なナビゲーションを避ける モジュール同士が協調動作する場合、それらを他モジュールから呼び出すようなことは避ける。 デメテルの法則では、モジュールは協調動作するモジュールに対する情報のみを知るべきである。システムの全体像を知る必要はない。 ### Java #### J1:ワイルドカードを使って、長いimportのリストを避ける ワイルドカード「*」でクラスやパッケージを指定することで、importリストの冗長化を防げる。 #### J2:定数を継承しない 定数は継承するのではなく、static importを用いる。 #### J3:定数とenum 定数にはenumを用いると良い。intよりも多くの表現力と柔軟性を持ったツールとなる。 ### Name:名前 #### N1:記述的な名前を選ぶ 名前は記述的である必要がある。 選んだ名前の適切さを頻繁にチェックする必要がある。 #### N2:抽象レベルに適切な名前を選ぶ 実装をそのまま表すような命名は避け、抽象度に応じて適切な名前をつける必要がある。 #### N3:可能な限り標準の用語を使用する 既存の規約や利用に基づいた名前は理解が容易である。 プロジェクトのための「ユビキタス言語」を定義し、それに従うと良い。 #### N4:はっきりした名前 関数や変数の働きを的確に表す名前を付けること。 多少長くても説明が付加されるほうが良い。 #### N5:広いスコープには長い名前を 名前の長さはその変数や関数のスコープの広さと比例するのが望ましい。 したがって、広いスコープで使用する場合は長い名前を与えるべきである。 #### N6:エンコーディングを避ける 名前の中に型やスコープの情報を盛り込まないこと。 #### N7:名前で副作用を示すべき 名前で示さないことをさせてはならない。 副作用があるならば、名前で表現すべきである。 ### Test:テスト #### T1:不十分なテスト テストは壊れる可能性があるところすべてに対して行うべきである。 #### T2:カバレッジツールを使用する カバレッジツールは、テストが不十分な部分を見つけるのに役立つ。 #### T3:些細なテストを省略しない 些細なものでもテストを書くこと。 #### T4:無視することを指定されたテストは、曖昧さへの問いかけである 要件が不明確な場合、要件に対する疑問点をコメントアウトしたり、@ignoreをつける。 #### T5:境界条件テスト 境界条件のテストは、細心の注意を払う必要がある。 #### T6:バグの周辺は徹底的にテストを バグは群がる傾向にあるため、発見したら関連する部分を徹底的にテストする。 #### T7:失敗パターンは何かを語る テストの失敗パターンから問題を発見できることがある。 #### T8:カバレッジパターンは何かを語る 成功したテストで、実行された行と実行されなかった行を比較すると問題を発見できることがある。 #### T9:テストは高速に実行できるべき 遅いテストは結局実行されなくなる。 高速なテストコードを書く必要がある。 ###### tags: `読書`
×
Sign in
Email
Password
Forgot password
or
By clicking below, you agree to our
terms of service
.
Sign in via Facebook
Sign in via Twitter
Sign in via GitHub
Sign in via Dropbox
Sign in with Wallet
Wallet (
)
Connect another wallet
New to HackMD?
Sign up