はむはむエンジニアぶろぐ

365日エンジニアリング

【書評】JavaとUMLで学ぶオブジェクト指向の考え方

入門的なオブジェクト指向の本だが、メタファーが分かりやすく専門用語がスッと入ってくる。
これから、オブジェクト指向を学ぶ人やオブジェクト指向を教える立場の人におすすめ。

はじめに

オブジェクト指向の概念を学ぶ前に、UMLを学ぶのは電気について何も知らずに回路図の読み方を習うようなもの。
まずは、オブジェクト指向の思考プロセスを理解することが大切。

第1章 オブジェクト指向のコンセプト

レガシーなコードでもオブジェクトラッパーで包み込むのが普通。
レガシーに引きづられることがないようにできる。

1.1 手続き型プログラミングとオブジェクト指向プログラミング

手続き型プログラミングは、データは別の構造に配置され、関数や手続きによって操作される。
データと手続きを分離している状態。
その為、関数内でグローバルなデータを扱うことが多くテストやデバッグが難しい。

オブジェクト指向プログラミングは、データと振る舞いを1つの完全なパッケージにして手続き型の問題を解決している。
メソッドへのアクセスは許可するが、内部の変数(プロパティ)へのアクセスを制限(カプセル化)できる。

しかし、設計が悪いとオブジェクト指向プログラミングで手続き型プログラミングを作成できてしまうので注意する必要がある。

1.3 オブジェクトとは何か?

手続き型の振る舞いは、関数。
オブジェクト指向の振る舞いは、メソッドである。

メソッドのユーザはメソッドシグネチャだけ知っていればよい。

  • メソッド名
  • メソッドの引数
  • 戻り値

どのようにメソッドが実装されているのか、メソッドのユーザは知らなくてよい。

要求の中にある名詞に着目し、名詞の中からクラス候補をピックアップし要求の中から動詞を抽出し各クラスに割り当てていく。
モデルは、既に存在するアイデアや構造を整理するためのツールである。

1.4 クラスとは何か?

オブジェクトの基本的な構成単位

  • データ
  • 振る舞い
  • メッセージ

メッセージ

メッセージはオブジェクト間の通信メカニズム。

オブジェクトAがオブジェクトBにメッセージ送信(メソッド呼び出し)する。
オブジェクトBは応答を戻り値として返す このような一連の流れをメッセージという。

1.6 カプセル化

カプセル化は、オブジェクトを利用する利点の一つ。
オブジェクトが持つ全ての属性とメソッドを公開する必要がない。
オブジェクト間での対話に必要なインターフェィスのみを公開し、それ以外の詳細部分を隠蔽できる。

オブジェクトが使用している変数やアルゴリズムを利用者に公開する必要はない。
そうすることで、堅牢性が増す。

インターフェィス

インターフェィスは、クラスの利用者がクラスとどのように対話するか規定する。
インターフェィスを介してのみ、クラスの属性を変更をしたり値を返す。
属性へのアクセスを制限することで、テスタビリティが向上する。

privateな属性やメソッドはクラスの利用者からすればインターフェィスではない。

実装(属性やアルゴリズム)を隠蔽するには、無駄を削ぎ落とした必要最低限のインターフェィスのみをオブジェクトから公開すること。
そうすることで、実装の変更可能性が上がる。
DBエンジンが変更されてもクラスの利用者は変更を意識する必要はない。

1.7 継承

オブジェクト指向の特徴の一つが再利用性である。
その再利用性で効果を発揮するのが継承。

継承はis-a関係。

1.9 コンポジション

オブジェクトを他のオブジェクトで構成していることをコンポジションという。
コンポジションは、has-a関係。

第2章 オブジェクト指向的な考え方

2.1 インターフェイスと実装の違いを理解する

インターフェイスと実装の違いを理解することは大事である。
クラスの設計では、利用者が知るべき必要があるものとそうではないものを分ける必要がある。

例えば、電化製品はコンセントというインターフェイスを利用することで電気を得ることができる。
電化製品を利用する場合、電気の発電方法と言った実装を知る必要はない。
火力発電が原子力発電になっても、電化製品には影響を受けない。

他にも車のハンドルをゲームのコントローラに変えてしまったら、利用者は混乱するがエンジンを交換しても利用者は交換されたことを意識すること無く運転できる。

  • インターフェィス: コンセントや車のハンドル
  • 実装: 発電方法やエンジン

コンセントというラッパーがいるので、電力を扱うための手段を火力発電にしても原子力発電にしても大丈夫だし交換可能である。
ちゃんと実装をラッパーするインターフェィスがあれば、データアクセス手段がファイルからDBMSに代わっても利用者に影響を与えない。

第3章 高度なオブジェクト指向の概念

3.1 コンストラクタ

コンストラクタで全ての属性を初期化する。
初期化はコンパイラに頼るべきではない。

3.2 エラー処理

プログラム実行時に検出された不具合を同ハンドルするのが良いか?

  • 不具合を無視する
  • アプリケーションを終了する
  • 回復を試みる
  • 例外をスローする

不具合を無視する

不具合は無視するべきではない。
エラーを無視したアプリは、最終的に異常終了するか不安定な動作を続けることになる。
また、動作不安定の原因特定が困難になる。

アプリケーションを終了する

不具合検出時に、アプリケーションがエラーメッセージを表示させる。
エラー時にファイルのクローズなど適切な処理を行う。
ただし、システムの利用者にはなぜ、停止したのか判断付かない。

回復を試みる

事前に不具合を検知して、プログラムで解決する。
例えば、0除算が発生するのであればif文で0を1に変換しておくなど。

ただし、コンテキストにより正しい処置なのか考える必要がある。

例外をスローする

例外は不具合を検出して処理するための手段。

第4章 クラスの構造

クラスがどのくらい役に立ち、他のクラスとどのように対話するか?

第5章 クラス設計のガイドライン

オブジェクト指向プログラミングの目標の一つが、頭のなかにある考えをモデル化することである。
クラス設計は、モデルを作成するための手段である。

よくある間違いは、クラスの属性を忘れて振る舞いだけのクラスを設計することである。
手続き型言語の作りと変わりがなく、カプセル化の概念に従っていない。

5.1 パブリックインターフェィスの決定

クラス設計において、パブリックインターフェイスを最小限に保つことが大事。
それにより、クラスがシンプルに保てる。

実装が変更されたために、アプリケーションの変更が必要になる設計は好ましくない。

5.3 エラー処理を考慮したクラスの設計

良いクラスは、エラーを予期して対策を用意している。(エラーハンドリグ)

5.5 拡張性を考慮した設計

ミドルウェアやハードウェアの情報がむき出しになった、インターフェィスはラッパーメソッドで隠蔽すること。
そうすることで、ミドルウェアやハードウェアが変わっても利用者のコードを変更する必要はない。
変更する必要があるのは、ラッパークラスだけである。

とにかく変数のスコープを小さくすることが大事。
グローバルであるほど、テストが困難になる。

第7章 継承とコンポジション

継承とコンポジションのどちらかを選択するか。

A dog is-a mammal(犬は哺乳類です)
Car gas-an engine(自動車はエンジンを持つ)

is-aは、階層関係。
has-aは、参照関係。

第8章 インターフェィスと抽象クラスを使った設計

8.3 契約

インターフェィス

インターフェィスの定義は、振る舞いを定めること。
Namaeable 名前を持っていることを定義しているインターフェィス。

  • 犬は哺乳類です (is-a)
  • 爬虫類は哺乳類ではない (not is-a)
  • 犬は名前をつけれる (Namaeable)
  • 爬虫類は名前をつけれる (Namaeable)

なので、インターフェィスは継承関係に関係がないものである。

第9章 オブジェクトの構築

9.1 コンポジション関係

コンポジションはhas-aである。
車を持っているという時に、エンジンとハンドルと4つのタイヤで構成される物という言い方をしない。
車を持っているで通じる。

コンポジションは、交換可能である。
ハンドルやエンジンを取り替えても車というものは変わらない。

9.2 段階的な構築

品質が良いソフトウェアを構築するには、できるだけシンプルにすること。

安定した複雑なシステムはほとんど、分割可能である。
もし、分割可能ではなく一体型であれば1つの大きいブラックボックスになってしまう。
一体型であるテレビデオが壊れた時に、テレビが原因で壊れたのか、ビデオが原因で壊れたの分からない。

一体型ではなく、コンポーネント構成であればテレビデオのビデオの部分だけを取り出して修理に出せる。
一体型よりも単純で、修理しやすいし、別のものに取り替えることも簡単である。

9.4 依存関係の回避

コンポジションを使用する場合、オブジェクトが強く依存しないように配慮する必要がある。
テレビデオは、テレビ部が壊れるとビデオも使えなくなる。
テレビだけを修理に出せない。ビデオも一緒に修理に出す必要がある。

一体型であると、便利な半面があるが安定性にかける欠点がある。