『オブジェクト指向のこころ』

オブジェクト指向のこころ (SOFTWARE PATTERNS SERIES)

オブジェクト指向のこころ (SOFTWARE PATTERNS SERIES)

まえがき

デザインパターンオブジェクト指向プログラミングの本当の姿を知り、習熟するにはかなりの努力が必要となる。
本書は、概念の根底に横たわっている、基本的な原理と動機、すなわち「やること」と「その理由」が理解できたときに、一気にその概念の理解が進むという信念・経験に基づいている。
そして、デザインパターンを考察することで、「オブジェクト指向の本当の姿」というものが理解できるようになる。

デザインパターンは孤立して存在しているのではなく、他のデザインパターンと強調し、より堅牢なアプリケーションを作成するための土台になるということも理解できるはず。 →保守が容易で、柔軟かつ完成度の高いソフトウェアを作成する準備が整う。

  • デザインパターンは個々の部品として積み上げていくものではなく、協調させて使用すべきもの

    問題を解決するためには、パターンをつむぎ合わせていかなくてはならない。
    ソフトウェア中のパターンはデザインパターンとして洗い出されているため、これらは設計(デザイン)時に適用されるものであると考えていたのです。つまりパターンというものは、クラス間の適切な関連として、設計時に導入されるものだと考えていたわけです。
    私の失敗は、問題領域からクラスを抽出し、その後、それらクラス群をつなぎ合わせて最終的なシステムを作ろうとしていたがために起こったのです。

  • 同時に学ぶメリット

デザインパターンの学習にはオブジェクト指向設計に関する十分な基礎が必要であると言われていた。 実際には、オブジェクト指向設計デザインパターンを同時に学んだ生徒は、単にオブジェクト指向設計だけを学んだ生徒よりも短期間でオブジェクト指向設計を学習できる。

  • 身についたとは

デザインパターンが見えてくる」ということは、デザインパターンによって解決できそうな問題を見るだけで、パターンから学んだ原則や戦略を用いて、パターンで表現された解決策が浮かんでくるようになるということです。

第1章 オブジェクト指向パラダイム

この章の目的

エキスパートが実践しているオブジェクト指向設計方法論を理解するための準備体操。
オブジェクト指向パラダイムは、構造化プログラミングの欠点を補完するために生まれてきたもの。
構造化プログラミングの欠点を明確にし、OOPの利点をより良く捉え、そのメカニズムをさらに深く理解する。

1. オブジェクト指向パラダイム以前

  • 機能分解(functional decomposition)
    機能分解=問題を小さな機能にブレークダウンし、その問題を構成する機能要素の洗い出しを行う。 大きな問題をそれらを構成する小さな問題に分割する。

  • 機能分解の落とし穴
    分解した機能を適切な順序で呼び出す、「メイン」プログラムが必要になる。
    メインプログラムにはすべてを正しく動作させる、機能の組み合わせと呼び出し順序を正しく制御するという、大きな責任が課せられる。
    →これがソースコードを複雑にする。

  • 委譲(delegation)
    部分機能に対してそれ自体の振る舞いに関する責任をもたせ、実行支持を行うだけで、後を任せる。
    機能分解でプログラムを作成すると将来の変更に対して、柔軟に対応することが難しい。

『多くのバグはソースコードの変更によって生み出されている。』
どれだけ頑張ったとしても、どれだけうまく分析したとしても、ユーザから全ての要件を引き出すことなんてできっこありません。明日のことなんて誰にも判らず、万物は流転していくのです。これは絶対不変の真理なのです。

  • 要求における問題

    私の30年にわたるソフトウェア開発経験で、要求について学んだ大事なことといえば、要求は常に変化するということです。

要求の変更にうまく対処できるソースコードを記述している開発者なんて、ほとんどいない。
変化は発生する。それに対応することが大事。

要求の変化に対して愚痴をこぼすよりも、開発プロセスを変え、変化に対して効率的に取り組めるようにするべきなのです。

ソースコードを工夫すれば、要求の変化から生まれる影響を最小化することも可能。

  • 変化に対応する
    巨大な関数をモジュールに分割する。
    モジュール化戦略=モジュールを用いて影響を局所化する
    図形を表示する処理。
例 :
入力 : 図形のタイプと詳細
switch (図形のタイプ)
    case 正方形 : 正方形の表示処理
    case 円形 : 円形の表示処理

新しく三角形の図形を表示したいという要求がでても、このモジュールだけを修正すればよい。
しかし、入力が異なる形式のタイプと詳細では対応できない。

  • 2つの重要な問題
    • 「低い凝集度」
      凝集度が低いクラスとは、関係のない作業が数多く詰め込まれたクラス。いわゆる神オブジェクト。
    • 「高い結合度」
      結合度が高い場合、好ましくない副作用(=機能やそれが使用するデータの変更により、他の機能に悪影響が及ぶ)を引き起こす。

機能分解を用いた場合、ある機能やデータを変更すると他の機能や他のデータに影響が及び、それがまた他の機能に影響を及ぼしていくという修正の連鎖が起こる。

  • 要求の変更に取り組む
    例 : 大学で開催されるセミナーの講師として任命される

構造化プログラミングの場合、以下の手順を取る。

1. 学生のリストを取得する
2. リストの学生に対して以下の処理を行う
    1. 学生の次のセミナーを調べる
    2. そのセミナーの教室を調べる
    3. 今の教室から次のセミナーの教室までの経路を調べる
    4. その学生に教室への生き方を伝える

現実にはこんなことはせず、セミナーの教室へ行く方法のポスターを教室の後ろに貼って、学生に自分で行動してもらう。
つまり、学生が自身の振る舞いに責任を持つ。責任を移行する(=委譲)。

2. オブジェクト指向パラダイム

問題領域を機能に分解するのではなく、オブジェクトに分類していく。

  • 利点 オブジェクト自体に責任をもたせて、物事を定義できる。オブジェクト内のデータによってその状態を識別でき、オブジェクト内のコードによって、適切な機能を実現できる。

優れた設計におけるオブジェクトは、明確に定義された責任を自分自身に保持しています。
オブジェクトには責任が課され、自らのことについて責任をもつことになるため、やってほしいことをオブジェクトに伝える方法が必要となります。

他のオブジェクトからも呼び出し可能なメソッドの集合を公開インターフェースと呼ぶ。
オブジェクト指向アプローチの場合、

1. 学生のコレクション(集合体)を実体化する
2. コレクションに対して、各学生を次の教室に移動させる旨の指示を出す
3. コレクションは、各学生に対して次の教室に移動する旨を伝える
4. 各学生は、
    1. 次の教室がどこか調べる
    2. 行き方を調べる
    3. そこに移動する

他の種類の学生を追加する場合にどうするか?
コレクションは様々な学生型を保持する必要がある。
コレクションに様々な種類の学生を参照させるには、どうすればよいか?
→ある型のグループを包含するような汎用の方を用意する。

f:id:kyamashiro:20181217001040p:plain
Studentオブジェクト
Student(学生)クラスは抽象、RegularStudent(学部生)クラス・GraduateStudent(大学院生)クラスは具象。is-aの関係にある。
この場合、派生クラスは、抽象クラスが定義した共通メソッドを使用するか、自らが実装したメソッドを使用するのか決定できる。

  • カプセル化
    公開インタフェースという考えからカプセル化の概念が導出される。
    public/protected/private
    カプセル化は内部のデータ構造を外部に公開しないという単なるデータ隠蔽ではなく、あらゆる種類の隠蔽を含む。
    Studentという抽象クラスによって、そのクラスから派生した具象クラスの型情報が隠蔽される。

  • ポリモーフィズム
    poly=多くの
    morph=形態
    Student(学生)は種類によって、異なった行動形態を取る。