元祖オブジェクト本の第2版です。原則・コンセプトで構成された本書と、方法論・実践で構成される別冊の二冊をあわせて『オブジェクト指向入門 完全版』になります。
本書は、オブジェクト技術の核となる4つのアイデア、構造化手法、信頼性の規律、認識論上の原則、分類の技法をソフトウェア工学からの立場で解説している言わばエンジニアリング本です。
結構小難しい言葉で書かれている部分があり、またページ数が多いため結構読むのが大変でした。その分本来のオブジェクト指向の考え方や良いプログラミングにつながる考え方が深くまで解説されていたりして、とても為になりました。
本書の対象読者は、オブジェクト指向技術に興味のある技術者や良い作法のプログラムを書きたいプログラマです。読むのは大変ですが損はない、むしろ読まないと損するくらいの本です。おすすめです。
真のモジュール性
プログラミングの世界でモジュールと言うと一連のサブルーチンのまとまりを指します。このモジュールを再利用し、モジュールの組み合わせでプログラムを組み立てるモジュール・プログラミングという考えがあります。再利用可能なモジュールは独立性が高く、異なるアーキテクチャに組み込んでも安定して稼動することが求められます。
モジュールに再利用可能という性質を組み込むための5つの基準(criteria)、5つの規則(rule)、5つの原則(principle)が存在します。そして、これらを満たしたものがクラスの基礎となります。
- サブルーチンの内訳
- サブルーチンは結果として戻り値を返すものと返さないものの二種類に分けられます。戻り値を返すものを関数(function)と呼びます。戻り値を返さない手続きをプロシージャ(procedure)と呼びます。
5つの基準
次の5つの基準を満たす設計手法は価値のあるモジュールを生み出す最低条件を満たします。
- 分解しやすさ
- 他システムへの依存性を低く抑えることができる
- 組み合わせやすさ
- 異なる環境においても自由に組み合わせることができる
- 分かりやすさ
- 独立性が高く、他のモジュールの知識を必要としない
- 連続性
- 変更の局所化が行えている
- 保護性
- モジュールの内部で発生したエラーの影響を閉じ込めることができる
基準 | 例 |
---|---|
分解しやすさ | 分解しやすさを満たす設計手法の有名な例はトップダウン設計(サブシステム分割) |
組み合わせやすさ | Unix の シェルコマンド(ls や more など) |
分かりやすさ | 悪い例として、順序に依存するような呼び出しを想定しているモジュールがある |
連続性 | シンボリック定数、動的配列 |
保護性 | モジュールの入り口で他システムからの入力をチェックする |
5つの規則
5つの基準から、モジュール性を保障するための次の規則が導き出されます。
- 直接的な写像
- 解こうとしている問題領域のモデルとモジュールの構造が写像(マッピング)されている
- 少ないインターフェース
- 他のモジュールとの連携を行う際できる限り少ない数のモジュールとのやり取りで済ませる
- 小さいインターフェース
- 他のモジュールと通信する場合、最小限の情報のみをやり取りすること
- 明示的なインターフェース
- 他のモジュールと通信する場合、そのことがインターフェースから明らかに分かること
- 情報隠蔽
- 適切な情報のみを公開すること。不要な情報は隠蔽しておくこと
5つの原則
基準、規則から次の5つの原則が導かれる。
- 言語としてのモジュール単位
- モジュールは言語の構文構造に対応していなければならない
- 自己文書化
- モジュールについての情報をモジュールの一部として作成する
- 統一形式アクセス
- 統一された表記によってアクセスできなければならない
- 開放/閉鎖の原則(Open-Closed Principle)
- 拡張に対して開き、修正に対して閉じている
- 単一責任の原則(Single Responsibility Principle)
- ひとつの役割だけをもつこと。変更の理由はただひとつでなければならない
再利用
再利用と一言で言っても、何を再利用するのか? 何が再利用できるのか? を考える必要があります。本書では次の7つの What への再利用の考えが述べられています。
- 人材
- 仕様
- 設計
- パターン
- ソースコード
- 専用の部品
- 抽象化されたモジュール
- 車輪の再発明は悪なのか?
-
よく「車輪の再発明は悪」といわれますが「車輪」って何のことを指しているのでしょうか? 本書で述べられている「仕様」のことなのか、「設計」のことなのかそれとも「ソースコード」のことなのか。
僕は「車輪」とは「仕様」のことだと理解しています。「走行抵抗を低くして対象に駆動力を与える」という「仕様」を再発明する必要はありません。でも、それを「車輪を円形で作成し、エンジンと連携させてコロコロ回るようにする」という設計や「空気によって地面に設置しなくても動く」という設計、設計を変えてつくることは全然良いことだと思っています。また、同じ円形の車輪でも「ゴムで作る」のと「木で作る」、実装を変えて作るのも全然良いことだと思います。
多重定義の役割
ルーチンの多重定義は顧客のための機能である。ある1つの概念を表す異なる実装を使うとき、顧客は1つの記述で済ませることができる。
つまり、意味は同じだけど実装を隠蔽する場合に多重定義(オーバーロード)を使うということです。
総称性の役割
総称性は供給者モジュールの作者のための機能である。ある特定の概念を表す1つの実装を異なる種類のオブジェクトに適用するときに、同じ供給者モジュールテキストを書くことが可能になる。
総称性(C++ のテンプレートや Java のジェネリクス)は開発者が楽するものという位置づけです。
抽象データ型
抽象とは、物体についての認知可能な性質を、そのほかの性質から、あるいは、その性質が当てはまる物体から分離することにほかなりません。
オブジェクトを抽象のまま扱うには、そのオブジェクトの概念に対する操作や概念の属性に注意を向けるようにします。操作がデータ構造を定義するものとして操作を定義すると良い抽象データ構造が定義できるようになります。
オブジェクト操作として考えなければならない概念として、モジュールの供給者(supplier)、モジュールの顧客(client)そしてプロトコル(契約)があります。モジュールはプロトコル(契約)を基本とした相互動作を行います。
契約による設計(Design by Contract)
すべてのモジュールにおいてお互いの権利と義務を明確に定義することで信頼性の高い大規模システムが実現できます。このような考えを「契約による設計(Design by Contract)」と言います。
契約による設計では表明(assertion)を使ってモジュールの仕様を表します。
- 事前条件
- ルーチン(メソッド)が呼び出される時に顧客が満たされなければならない条件
- 事後条件
- 事前条件を満たした状態でルーチンが呼び出された場合に供給者が満たさなければならない条件
- 不変条件
- ルーチン呼び出しによって変わらないオブジェクトの条件
事前条件
事前条件はルーチンの顧客が満たす
事前条件は、ルーチン(メソッド)の呼び出し側、つまり顧客(Client)が満たさなければならない条件です。呼び出し側はルーチンの事前条件を満たしているかどうかを確認してからルーチンを呼び出します。
ルーチンの供給側は事前条件を確認できる操作を公開する
ルーチンの供給側は顧客が事前条件を満たしているかどうかを判断できる操作を公開しなければなりません。公開されている操作で顧客が事前条件を確認できない場合には、事前条件がおかしいか公開している操作が足りません。
事前条件は人間の入力、システム連携には使用しない
契約はルーチン間(供給者と顧客)で取り交わすものです。ここでいう供給者と顧客とはソフトウェア同士のことです。ソフトウェアと人間、ソフトウェアと外界の間には契約は成り立ちません。
人間や外界のシステムが相手の場合には入力検査の仕組みを導入しなければなりません。たとえば、入力チェックモジュールによるフィルタなどが考えられます。
事後条件
事前条件が満たされない場合の事後条件は保証されない
事前条件が満たされないでルーチンが呼び出された場合、ルーチンの事後条件は保証されません。つまり供給者側はそれをエラーにしても良いし、でたらめな値を返してもよいということです。
事前条件が満たされたのに事後条件を満たせない場合
事前条件が満たされているのに、事後条件が満たせない場合には、供給側は例外をスローします。
表明違反の規則
- 実行時の表明違反は、そのソフトウェアにバグがある証拠である。
- 事前条件違反は顧客側にバグがある証拠である。
- 事後条件違反は供給側にバグがある証拠である。