# クリーンコード 第12章、第13章 ## 第12章 創発 ### 創発的設計を通じて洗練する ケント・ベックが提唱した単純な設計規則は以下のとおりである。 ### 全テストを実行する **システムを検証するテストが包括的になされる**ことで、システムの正常化につながる。 テストが必要であり、常に流し続ける必要がある。 テストが出来上がれば、リファクタリングができる。 インクリメンタルにリファクタリングを施すことができる。 ### 重複の排除 重複は様々な形で現れる。 同一のコードはもちろん、似通ったコードも手を加えることで、さらに似たものにすることができ、リファクタリングが簡単になる。 また、実装の重複も、工夫を加えて一つの実装にまとめられる。 **洗練されたシステムを作り上げるには、重複を排除するという意思が必要である。** 小さな重複を取り除き、小さな再利用が行われることで、複雑さは大幅に緩和される。 テンプレートメソッドパターンを用いると、複雑で大規模な重複を修正できる。 ### 表現に富む 自分自身が理解できるコードを書くのは簡単だが、将来の保守担当者が見て分かるようなコードを書かなくては、保守コストが増大してしまう。 **より良い命名や小さな関数・クラス、デザインパターン等の標準の利用、適切な単体テストによって、将来にわたりコードの「意図」を伝えることができる。** 但し、表現力を発揮するために最も重要なことは「試す」ことである。 ### クラスとメソッドを最小限に 関数とクラスを最小限にしようとすると、あまりにも大量に出すぎてしまう状況に陥ることがある。無益な独断の結果から生じていることもある。 **非常に重要ではあるが、設計規則では優先度が最も低い**ことを念頭に置く。 ## 第13章 同時並行性 ### なぜ同時実行性が必要なのか **同時実行性**とは、「**何をするのか**」と「**いつするのか**」を分離する戦略である。 すなわち、**コード内の処理と、実行タイミングを分離させる**ことで、アプリ構造とスループットの改善に役立つ。 #### 神話と誤解 以下は、同時並行性の**誤った解釈**の例である。 * 同時並行性は常にパフォーマンスを改善する?:待ち時間が大量にあり、複数スレッドあるいはプロセッサで共有できる場合のみ。そんなに単純ではない。 * 同時並行プログラムを書く場合の設計変更は不要?:シングルスレッドとは大きく構造が異なる場合があり、その場合は与える影響も大きい。 * コンテナ内の処理であれば、さほど重要ではない?:コンテナが何を行い、負同時更新やデッドロックにどのような対処を行っているかは知るべきである。 * 同時並行性はメリットばかり?:余分なコードの発生やオーバーヘッドが避けられず、正しい同時並行性の確保は複雑になる。また、バグの再現性が無くテストが困難であったり、設計戦略自体を見直す必要が生じる可能性もある。 ### 同時並行性防御原則 #### 単一責務の原則 まずは**単一責務の原則(変更の要因が一つだけ)** についてである。 同時並行処理は複雑で、それ自体がライフサイクルを持つため、1つの変更の要因となる。 また、同時並行処理以外のコードと難しさの質が異なる。 したがって、**同時並行処理を他処理から分離することには価値がある。** #### 帰結:データのスコープを狭めよ syncronizedキーワードを使って、共有オブジェクトを操作するクリティカルセクションを保護する。そしてその**数をできる限り抑える。** 数が多くなると、保護の設定ミスや原因調査が難しくなる。**データのカプセル化を徹底すべし!** #### 帰結:データのコピーを活用せよ データをコピーし、読み取り専用にする。 あるいはオブジェクトをコピーして、複数スレッドの結果をコピーに集め、1つのスレッドでマージする。 #### 帰結:スレッドはできる限り独立させよ 全てのスレッドが独自の振る舞いを行えば、共有は必要ない。 それぞれのスレッドが1クライアントからの要求を処理し、必要なデータは共有されていないデータソースから読み込まれ、ローカル変数に格納される。 この場合、同期は不要である。 ### 使用しているライブラリを知る #### スレッドセーフなコレクション Java5では、同時並行開発のための改良が大量に施されている。 スレッドセーフなコレクションは、マルチスレッド処理に対応しており、かつ高速に動作するように設計されている。 ### 実行モデルを見分ける #### プロデューサー・コンシューマー型 1つ以上のプロデューサースレッドが作業を生成して、バッファやキューに格納する。一方で1つ以上のコンシューマースレッドが作業をキューから取り出して完了させる。 **プロデューサーが生成作業、コンシューマーが作業実行を担う。** 両者が扱うキューは共有であるため、キューの状態によって待機状態が生じる。 **状態に関するシグナル通信が、プロデューサー・コンシューマー間でキューを通じて行われるようにしなければならない。** #### リーダ・ライタ型 リーダが読み込み作業、ライタが書き込み作業を担う。 ライタが更新作業を完了させるまで、リーダは読み取りを行わないようにする。 リーダが常にいると、ライターが**飢餓状態(いつまでも実行されない)** となる一方、 ライタの更新作業が長時間であったり、高頻度で発生したりすると、**スループット上での問題**が発生しやすい。 **これらのバランスをうまく調整する必要がある。** ### 同期化メソッド間の依存関係に注意 1つの共有クラスに、複数の同期化メソッドがある場合、バグを引き起こす可能性がある。 **基本的に複数の同期化メソッドの使用は避けるべきである。** 代わりに、クライアントベースロック、サーバベースロックのどちらか、あるいはサーバベースロックの一種で、中間層を設定するサーバ適合がある。 ### 同期化セクションを小さくする **クリティカルセクションの作成数は、なるべく少なくする。** ロックの生成に時間を要し、オーバーヘッドが生じやすいためである。 ### 正確な終了処理コードを書くのは難しい 複数処理をきれいに終了させるのは難しい。 例として、デッドロックによる立ち往生がある。 相手の動作の結果に応じて終了するシステムの場合、相手が動作の結果をもたらすよりも早く、別の要因でデッドロックや終了命令を受けて終了した場合、永遠に終了できなくなる恐れがある。 **こうした状況は早期から把握し、対処する必要がある。** ### スレッド化されたコードのテスト テストによるコードの正確性を担保するのは難しい。 しかし、優れたテストの実行で、リスクを低下させることはできる。 #### 怪しい失敗はスレッドが原因として扱う スレッド化されたコードは、「簡単には起きないエラー」を起こす。 テストで再現できない偶発的なエラーも多いため、「一過性の問題」と処理してしまうことも多いが、潜在的な欠陥を増やすことに繋がってしまう。 一過性の問題と片づけないこと。 #### 最初にスレッド化されていないコードを完成させる まずは、スレッド化されていない通常のコードを完成させ、マルチスレッド以外の環境で動作することが望ましい。 #### スレッド化されたコードは、差し替え可能である 同時並行性を持つコードは、どのような環境においても実現できるようにしておくのが望ましいため、場合に応じて差し替えを可能とするべきである。 #### スレッド化されたコードは、チューニング可能である スレッド数を簡単に調整できるようにして、異なる設定でのパフォーマンス測定ができるようにするべきである。 #### プロセッサよりもスレッドの数を多くする プロセッサのコア数よりも多い数のスレッドを処理する場合、コア間でタスクスイッチが発生する。この状況下で行う事でバグを発見しやすくなる。 #### 異なるプラットフォームで実行する WindowsやMac、AppleやAndroid等、様々なプラットフォームで実行テストを行い、プラットフォーム特有の実装方法に対応する必要がある。 #### コードに対して様々なことを試してエラーを発生させる 膨大な実行経路のうち、エラーが発生するのは僅かである。 Object.wait(他にsleep, yield, priority等)()といったメソッドを実行することで、実行順序を変更でき、バグを発見しやすい。 #### 手作業で埋め込む 難易度の高いコードのテストに、手動で上記の関数を埋め込む方法。 そもそも埋め込み場所が難しく、問題が見つかる可能性は低い。 テスト時のみ実施し、複数の構成を組み合わせて実行するべきである。 #### 自動実行する アスペクト指向フレームワークでは、プログラム的にコードを変更できる。 コードに異なる順序、異なるタイミングで揺さぶりをかけることで、エラーを発見できる可能性は高まる。 ###### 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