読者です 読者をやめる 読者になる 読者になる

【読書メモ】オブジェクト指向のこころを読んで、覚えておきたいと思ったこと

オブジェクト指向

新しい原理を学ぶときには主に2つの方法がある。

  1. 原理を覚えてから、原理を使った具体例を試していく方法
  2. 良い具体例を観察して、それらの背後にある原理を理解する方法

オブジェクト思考のこころでは、2の方法でオブジェクト指向パラダイムを学ぶことができる。

システムの変化を特定部分に封じ込めることで、そこ以外の既存部分を再利用する

要求の追加や変更が発生するたびに、システム全体を見なおして影響する部分を探しだしたり、テストの時点で既存機能に悪影響が発生していないことを確認しなければならない場合、莫大なコストがかかることになる。こういったことは、なんとしてでも避けるべき。つまり、将来変更が起きそうな部分や、開発時点で詳細が見えていない部分を何らかの形でカプセル化し、それらの詳細を意識することなしに使用できるようにしておけば、システム変更が発生しても影響範囲を局所化することができる。

どう変わるかはわからなくても,どこが変わるかは予想できる

変化が起きそうな部分を封じ込め,他のソースコード部分から隔離することにより,オブジェクト指向のもつ特徴を最大限に活用できるということを意味している.

オブジェクトを使用する利点は,オブジェクト自体に責任を持たせてものごとを定義できる点

あるレベル(概念)でコミュニケーションを図り,他のレベル(実装)でそれを遂行することにより,リクエストする側は何が起こるのかを正確に知らなくてもよくなり,何が起こるのかの概略だけを知っておけばよくなる.オブジェクトには自らに関するデータ,および要求された機能を実装するためのメソッドの多くは,他のオブジェクトからも呼び出し可能となる.

コンポジションと集約の違い

コンポジションは、保持されているものが保持しているものの一部である場合。(たとえば、自動車とタイヤの関係) 集約は、独立で存在できる何らかのコレクションがある場合。(空港とそこにいる飛行機の関係)

FacadeパターンとAdapterパターンの違い

Facadeパターンはインターフェースを簡素化し,Adapterパターンは既存のインターフェースを他のインターフェースに変換する.

カプセル化とはあらゆるものを隠蔽すること

データを隠蔽することも可能. しかし以下のものを隠蔽することもできる.

  • 実装
  • 派生クラス
  • 設計の詳細
  • 実体化の規則

流動的要素の保持先

一般的にはデータに流動的要素を当てはめがちであるが,オブジェクトにすることもできる. 流動的要素をオブジェクトとすることで,そのオブジェクトに振る舞いを追加することで,他のデータやオブジェクトへ詳細を隠蔽できる.

変更のことを考慮して設計するアプローチ

変更は将来必ず起こるものであるという前提に立ち,どこに変更が発生するのかを予測しようとしている.このアプローチはGoF本に記述されている以下の原則に基づいている.

  • 「実装を用いてプログラミングするのではなく,インターフェースを用いてプログラミングしてください」
  • 「クラス継承よりもオブジェクトの集約を多用してください」
  • 「あなたの設計において,何を流動的要素とするべきかを考察してください」
    このアプローチは再設計せず何を変更可能にするのか考えるもの.ここで着目しているのは流動的概念のカプセル化であり,多くのデザインパターンでテーマとなっているもの.

デザインパターンの使用の際には,まず問題そのものを理解する必要がある

デザインパターンを見る場合,大抵の人はそのパターンが提供する解決策を重視する.デザインパターンは,特定の問題に対する優れた解決策を整理したものであると言われているため,こう考えるのももっともである.
しかし,これを前提にすると間違った方法へ進んでしまう.ある問題に対する解決策を適用する前位に,まず問題そのものを深く理解しなければならない.つまり,問題そのものを理解することなしに,問題領域の中からパターンが適用できそうな部分を探し出すというアプローチをとってしまうと,何をするかは検討がつくものの,いつそれを使うのかやなぜそれを使うのかといったことがわからないままになってしまう.

switchは抽象化の必要性を示す赤信号となりうる

switchは

  1. ポリモーフィズムの導入
  2. 責務の見直し

を検討するべき赤信号となり得る. この場合,抽象化を行ったり,他のオブジェクトに責務を委譲したりするといった,より一般的な解決策を考慮する必要がある場合が多い.

パターンで考えるための手順

  1. パターンの洗い出し.
    • 問題領域に存在するパターンを見つけ出す
  2. パターンの分析・適用
    • パターンの並べ替え(コンテキスト生成順)
    • パターンの選択と設計の拡張
    • 追加のパターンの洗い出し
    • 繰り返し
  3. 詳細の追加
    • 必要に応じて設計に詳細を追加する.
      メソッドとクラス定義の拡張も行う.

Open-Closed Principle(OCP):開放/閉鎖原則

この原則をわかりやすく言い換えると,モジュール,メソッド,クラスは拡張性という観点から見た場合,見通しの利くよう(open)になっており,変更という観点から見た場合,閉鎖的(closed)になっているべきであるということになる.つまりソフトウェアは,変更することなく拡張できるように設計するべきであると述べている.
この原則は,一見すると矛盾しているが,例えばBridgeパターンでは,既存クラスに一切手を加えることなく,新たな実装を追加できる.Open-Closed Principleの本質的な意味合いは,新たな機能を個別に,すなわちモジュール化された形で追加していけるようにすることで,結合コストを最小化したソフトウェア設計にするということである.

依存性の逆転原則

  • 高次のモジュールは,低次のモジュールに依存してはならない.高次のモジュールと低次のモジュールは,いずれも抽象的側面に依存すべきである.
  • 抽象的側面は詳細に依存してはならない.詳細が抽象的側面に依存すべきである.

設計上の意思決定方法

複数の実装から適切なものを選ぶ場合,多くの開発者は「どの実装が優れているのか」という疑問をもつ.しかし,これは適切な疑問ではない.ある実装が他の実装より常に優れているというわけではない.持つべき疑問は,実装ごとに「この実装は,どういった状況において他の実装よりも優れているのか」となる.その後,「自分自身の抱えている問題領域に最も近い状況はどれなのか」と考えることになる.

Decoratorパターンの使い所

Decoratorパターンを使えば,何らかの制御メソッドを使って追加機能を制御するのではなく,必要な機能を必要な順で連結することが可能になる.こういった機能の連鎖は,Decoratorパターンによって動的に作成されることになるため,機能の連鎖とそれを使用するクライアントを切り離すことができる. また,このパターンによって,連結対象コンポーネントと連鎖構造を切り離すことも可能になる.これにより柔軟な形でコンポーネントを使用できるようになる.

開発を2つの手順に分割するアプローチ

オブジェクトの生成と管理を検討する段階で従うべき一般的な規則がある それは,「オブジェクトは,他のオブジェクトを生成・管理するか,他のオブジェクトを使用するかのどちらかのみ行い,双方を行ってはいけない」というものである.

そのため開発アプローチも2つに完全に分割される. 1. オブジェクトと,その動作方法を定義する 2. 適切な状況下で適切なオブジェクトを実体化し,それらオブジェクトが共有される場合は管理を行うファクトリを開発する.

手順1で開発するコード自らは,どういったオブジェクトが実体化されているのかについて関知しない. 手順2で開発するコード自らは,実体化したオブジェクトがどのように動作するかについて関知しない. それぞれのコードは凝集度が高くなっている.

パターンを覚える必要はない

パターンの原則を理解していれば,知らないパターンが問題領域に含まれていたとしても,そのパターンと同じ解決策を自分自身で導き出せるようになる. さまざまなクラス図やパターンを覚えたり,分厚い参考書を手元においておくことよりも,デザインパターンの基本原則を理解しておくことのほうが重要.

パターンを学習する際に自問すべきフォース

  • このパターンが隠蔽しているのはどのような実装か?
  • このパターンにはどういった共通性が存在しているか?
  • このパターンにおけるオブジェクトの責務は何か?
  • これらのオブジェクト間にある関係は何か?
  • パターン自体は,どのようにしてコンテキストに基づく設計を具体化しているか?

yusuke-ujitoko.hatenablog.com