【上巻】Codeコンプリート第5, 6, 7章レビューメモ

【上巻】Codeコンプリート熟読中のメモになります。

かなり本書の文になるのでまずいようならご連絡頂ければと思います。

【上巻】Codeコンプリート第1, 2, 3, 4章レビューメモ

第5章 設計について書かれている

  • どのような方法をとるとしても、小規模なプロジェクトも大規模なプロジェクトと同じように、入念に設計すればその恩恵 を受ける。そして、設計を明示的なアクティビティとして意識することで、設計から得られる利益は最大になる。
  • これほど適した説明はないと感動
    • 「ソフトウェア設計」という言葉は、コンピュータソフトウェアの仕様を運用可能なソフトウェアに変化させるための概念、発想、または工夫を意味する。設計とは、要求をコーディングやデバッグと結び付けるアクティビティである。最上位レベルの設計が正しければ、複数の下位レベルの設計を確実に追加できる構造が実現される。優れた設計は、小規模なプロジェクトに役立ち、大規模なプロジェクトにはなくてはならないものである。
  • 複雑さに2つの方向から取り組むことを提案している。
    • 一度に対処しなければならない本質的な複雑さを最小限に抑える。
    • 偶発的な複雑さを必要以上に増やさない。
  • ソフトウェアでは複雑さへの対処が最重要事項であり、他の技術目標は二の次であることを理解すれば、多くの設計問題が単純になる。
  • 良い設計の一つで「無駄のなさ」(これも感動)
    • 本が完成するのは、他に追加できるものがなくなったときではなく、これ以上削除するものがなくなったときである。
  • 設計のレベルに関する説明
  • 抽象化の説明が面白い
    • 「家」は、窓、ドア、壁、配線、配管、断熱材、そしてそれらを1つにまとめる方法の抽象化である。「ドア」も、ちょうつがい、ドアノブ、四角い素材を決まった方法で組み合わせた抽象化である。そして「ドアノブ」も、特定の形状の真鍮、ニッケル、鉄、スチールの抽象化である。 人間は絶えず抽象化している。そうしなければ、玄関のドアから出入りするたびに木の繊維やニスの分子、スチールの分子を1つひとつ相手にしなければならず、家の出入りも一苦労である。
  • 「何を隠ぺいすべきか」と自問する習慣をつけよう。
    • コンストラクションレベルでは、リテラルの代わりに名前付きの定数を使用するきっかけとなり、クラスレベルでは、ルーチンやパラメータに適切な名前を付けるのに役立つ。システムレベルでは、クラスやサブシステムの分解や相互接続への決断を手引きする。
  • 一般的なデザインパターン
  • トップダウン方式
    • 分解戦術
    • 人は大きなものを小さく分解するのが得意
    • トップダウン方式は、最初は簡単であるものの、下位レベルの複雑さが押し寄せてきて、それらの影響が必要以上に事態を複雑化させることがある。
  • ボトムアップ方式
    • 組み立て戦術
    • ボトムアップ方式は、最初は複雑であるものの、その複雑さを早い段階に明らかにしておくと、(その複雑さがシステム全体を粉砕するようなものでなければ)上位クラスをうまく設計できる。
  • プロトタイプは、設計の問題が十分に明確でないと、うまくいかない。
    • ここで言うプロトタイプとは[* 必要最低限のコードを作成する]もの。なので、[* 設計の問題が十分に明確になっていない]と言うことはプロトタイプで確認する問題が明確でないと言うこと。イコール必要最低限のコードで作成できない
  • 設計を、やっかいで、ルーズで、ヒューリスティックなプロセスとして扱うこと。最初に思い付いた設計で満足してはならない。第三者と協力すること。単純さにこだわること。必要であればプロトタイプを作成すること。反復、反復、反復を繰り返す。そうすれば、自分の設計に満足できるだろう。
  • 設計のプラクティス
    • 最初の作業ではなく、何度か作業を繰り返して、最も良いものを選択したか。
    • システムを何種類かの方法で分解し、どの方法が最も良いか試したか。
    • 設計問題にトップダウン方式とボトムアップ方式の両方で取り組んだか。
    • システムの危険な部分やよくわからない部分のプロトタイプを作成し、特定の問題に答える必要最小限の使い捨てコードを作成したか。
    • 設計を第三者による公式または非公式のレビューにかけたか。
    • Wiki、電子メール、フリップチャート、デジタル写真、UML、CRCカード、またはコードのコメントなど、適切な方法で設計作業を記録したか。
  • 設計の目標
    • アーキテクチャレベルで識別され、先送りされた問題に、設計が十分に対処しているか。
    • 設計が階層化されているか。
    • プログラムをサブシステム、パッケージ、クラスに分解する方法に満足しているか。
    • クラスをルーチンに分解する方法に満足しているか。
    • クラスどうしの結合度が最小限になるように設計されているか
    • クラスやサブシステムは他のシステムで利用できるように設計されているか。
    • プログラムは保守しやすいか。
    • 設計に無駄はないか。すべての部分は本当に必要か。
    • 設計は標準的な手法を用いているか。風変わりな理解しにくい要素を使用していないか。
    • 全体的に見て、設計は偶発的な複雑さと本質的な複雑さを最小限に抑えるのに役立っているか。
  • 第5章まとめ
    • ソフトウェアの鉄則は、複雑さに対処することである。これには単純さにこだわった設計が大きく貢献する。
    • 単純さは一般に2つの方法で実現される。一度に頭に入れなければならない本質的な複雑さを最小限に抑えること。そして、偶発的な複雑さを必要以上に増やさないことである。
    • 設計はヒューリスティックな作業である。1つの手法に固執すると、創造性が失われ、プログラムも悪い影響を受ける。
    • 良い設計は反復的である。さまざまな設計を繰り返し試してみるほど、最終設計は良くなる。
    • 情報隠ぺいは特に価値の高い概念である。「何を隠ぺいすべきか」を自問することによって、難しい設計問題の多くが解決される。
    • 本書以外にも、設計に関する興味深い情報がいろいろ提供されている。本書の見解は氷山の一角にすぎない。

第6章 クラスについて

  • クラスとは、強くて明確な責務(responsibility)を共有するデータとルーチンの集まりである。
  • 抽象化、カプセル化が出るが抽象化の方が重要な印象。
  • クラスの使用法を理解するためにクラスの実装を調べていることに気付いたら、それはインターフェイスに対するプログラミングではない。インターフェイスを通じて実装に対するプログラミングをしているのである。
    • 理想、意識して設計、プログラミンする事は分かるが、実際にこれほどのプログラムは組めるものなの?
  • 強すぎる結合度に注意する
    • クラスやメンバへのアクセスをできるだけ制限する。
    • フレンドクラスは結合度が強いので使用しない。
    • スーパークラスでは、データをprotectedではなくprivateで宣言し、そこから派生したサブクラスとスーパークラスとの結合度を弱める。
    • クラスのパブリックインターフェイスでメンバデータを公開しない。
    • カプセル化の意味的な違反に注意する。
    • 「デメテルの法則」に従う。
  • 包含(「has a」の関係)
  • 継承(「is a」の関係)
    • クラスが継承される設計になっていなければ、そのクラスを継承できないようにする。
    • C++とJavaでは、ある意味、プログラマがオーバーライド不可能なメンバルーチンをオーバーライドできる。基底クラスにprivateで定義されたルーチンがあれば、派生クラスでそれと同じ名前のルーチンを作成することができる。派生クラスのコードを読んだプログラマは、そのようなルーチンを見て混乱するだろう。なぜなら、ポリモーフィックでなければならないものに見えて、実際にはそうでなく、同じ名前が付いているだけなのだ。このガイドラインを別の方法で言い換えるなら、「基底クラスのオーバーライド不可能なルーチンの名前を派生クラスで使用しない」となる。
      • 派生クラスでは基底クラスのメンバルーチンと同じ名前のルーチンは作成できないような仕様に何でしてないのか!?派生クラスでなければよしとすれば良さそうなのに。
    • 「継承は、プログラマの第一の責務である複雑さへの対処にマイナスに働く傾向がある」為ルールが多い
      • 嫌なイメージしかないw
  • クラスを作成する理由
    • プログラムの複雑さを低減する事
      • 抽象オブジェクトをモデリングする
      • 現実世界のオブジェクトをモデリングする
      • 複雑さを緩和する
      • 複雑さを分離する
      • 実装の詳細を隠蔽する
      • 変更による影響を限定する
      • グローバルデータを隠蔽する
      • 引数の受け渡しを合理化する
      • 制御を一元化する
      • コードの再利用を促進する
      • プログラムのファミリを計画する
      • 関連する操作をパッケージにまとめる
      • 特定のリファクタリングを実行する

第7章 高品質なルーチン

  • ルーチンを作成する理由まとめ
    • 複雑さを低減する。
    • 中間部分をわかりやすく抽象化する。
    • コードの重複を避ける。
    • サブクラスを作成しやすくする。
    • 処理順序を隠ぺいする。
    • ポインタの処理を隠ぺいする。
    • 移植性を向上させる。
    • 複雑な論理評価を単純にする。
    • パフォーマンスを向上させる。
  • 凝集度
    • 一般的に容認される凝集度
      • 機能的凝集度
        • 最も強い最高品種の凝集度。ルーチンが処理を1つだけ実行する場合
      • 情報的凝集度
        • ルーチンが決まった順序で実行しなければならない処理で構成され、それぞれの段階でデータを共有し、同時に実行したのでは機能を完全に達成できない場合
      • 連絡的凝集度
        • ルーチン内の処理が同じデータを使用するが、それ以外に関連性がない場合
      • 時間的凝集度
        • 同時に実行される複数の処理をルーチンにまとめる場合
    • 上記の凝集度が一般的に容認される凝集度であるが、機能的以外の凝集度をより強い凝集度にする為の方法は、それぞれのルーチンの中身を機能的凝集度にまとめる、分割すること。
    • 一般的に容認できない凝集度
      • 手順的凝集度
        • ルーチンの処理が特定の順序で実行される場合である。例としては、社員の名前を取得し、次に住所を取得し、最後に電話番号を取得するルーチン
      • 論理的凝集度
        • 複数の処理が1つのルーチンに詰め込まれ、渡された制御フラグによっていずれかの処理が選択される場合である。
        • あくまでルーチンにロジックが含まれる場合の事。ロジックが含まれず制御フラグによって別ルーチンが呼ばれるだけのルーチンは一般的に「イベントハンドラ」と呼ばれ良い設計と言える。
      • 暗号的凝集度
        • ルーチン内の処理どうしにそれとわかるような関係がない場合
  • 良いルーチン名
    • ルーチンが行うことを全て説明する
      • ルーチンの名前にすべての出力と副次効果を反映させる。
    • 関数名には戻り値の説明を反映させる
      • ここまでを徹底するとルーチン名がかなり長くなる気がする。
    • 引数は入力、変更、出力の順に配置する
      • この順序は、データの入力、データの変更、結果の返送という、ルーチン内での処理の順序を反映している。

感想

設計について、考えたり分かってるつもりでいた事が露骨に浮き彫りになるくらい、詳しく理論的に説明してくれており、かなり良い!
またクラスやルーチンの設計方法などの説明に入ってきたことでより理解しやすく、かつ読んでて面白くもなってきた。

スポンサーリンク
レスポンシブ