はじめに
オブジェクト指向について改めて勉強する必要があると思い、オブジェクト指向でなぜつくるのか 第3版を読むことにしました。
第1版と第2版は読んだことが無いのですが、ちょうど第3版が出版されたばかりのタイミングなので第3版にしました。
第1章 オブジェクト指向はソフトウエア開発を楽にする技術
- オブジェクト指向はモノ中心に組み上げる開発手法で、ソフトウエアの保守や再利用をしやすくすることを重視している
- 最初はプログラミング言語として登場したが、今ではソフトウエア開発全体をカバーする統合的な技術になっている
オブジェクト指向が難しい理由
その1: プログラミング言語の仕組みが複雑
- オブジェクト指向には以下のようにたくさんの仕組みがあるため、理解して使いこなせるようになるまで時間がかかる
クラスとインスタンス、インスタンス変数、メソッド、コンストラクタ、可視性、継承、スーパークラスとサブクラス、ポリモーフィズム、パッケージ、例外、ガベージコレクションなど
その2: 比喩を使った説明による混乱
- 説明の際に以下のような比喩が強い印象として残るため、実際のプログラミングの場面でどう便利なのかが伝わりづらい
動物がスーパークラスで、哺乳類や魚類がサブクラス。卵を産みミルクで子供を育てるカモノハシは、は虫類と哺乳類を多重継承している
その3: オブジェクト指向というコンセプトが抽象的
- オブジェクト指向の「モノ中心」「モノ指向」というコンセプトは、現実世界の様子とよく似ている
- しかし、全く同じではない
- そのため、現実正解で人間が行う仕事をそのままオブジェクト指向でプログラミングしようとしても、上手くいかない
第2章 オブジェクト指向と現実世界は似て非なるもの
- オブジェクト指向を現実世界になぞらえた説明はあくまでも比喩として割り切った上で、「プログラミングのための仕組み」として理解するのがおすすめ
クラスとインスタンス
- オブジェクト指向と現実世界では、クラスの位置付けが異なる
- オブジェクト指向におけるクラスはインスタンスを作るための仕組みであり、インスタンスが帰属するクラスは1つだけ
- しかし現実世界においては、先に具体的なモノ(インスタンス)があった上で、それを見る側の立場や興味の違いによって様々な基準で分類(クラス)している
##メソッド
- メソッドの呼び出し(メッセージパッシング)についても、現実世界とは異なる
- オブジェクト指向では、メソッドを呼び出す(指示を出す)とその通りに動く
- しかし、現実世界では人や動物は必ずしも指示を受けて動くわけではない
- 自らの意思で動くこともあれば、指示を受けてもその通りに動かないこともある
第3章 OOPを理解する近道はプログラミング言語の歴史にあり
- OOP(オブジェクト指向プログラミング)は、プログラミング言語の進化の歴史の中で必然性を持って登場した
プログラミング言語の進化の歴史
表現力の向上
- プログラミング言語は最初
機械言語
→アセンブリ言語
→高級言語
と進化してきた - ここまでの進化は、以下のようにプログラミング言語の表現力向上を追求してきた
コンピュータにやらせたい仕事を、いかに簡単に、かつ人間に親しみやすい方法で表現するか
- しかし、これだけでは1960年代後半に宣言された
ソフトウエア危機
を救うことはできなかった
保守性と品質の向上
-
ソフトウエア危機
を乗り越えるため、次の構造化言語
への進化ではこれまでとは異なる「保守性を向上させる」という方向性を示す必要性があった - この背景には、プログラムの寿命が当初よりも長くなったことがある
- コンピュータが登場した当初は、毎回ゼロからプログラムを作り直していた
- しかしソフトウエアに対する要求が膨らんでプログラムの規模が大きくなると、毎回全てを作り直すしている暇はなくなり既存のプログラムを改修していくケースが増えた
- このようにプログラムの寿命が伸びたことにより、複雑さを避けて間違いを起こさないようにする「品質の向上」も重視するようになった
グローバル変数と貧弱な再利用の問題
-
構造化言語
になっても解決できない「グローバル変数」と「貧弱な再利用」という2つの問題があった - この2つの問題を打ち破ったのが、オブジェクト指向である
第4章 OOPは無駄を省いて整理整頓するプログラミング技術
OOP(オブジェクト指向プログラミング)の三大要素
- OOPは、それまでのプログラミング言語には無かった
クラス
・ポリモーフィズム
・継承
という3つの優れた仕組みを持っている - この3つの仕組みは、構造化言語で解決できなかった「グローバル変数」と「貧弱な再利用」という2つの問題を解決するためのものである
クラス
クラスとは、「まとめて、隠して、たくさん作る」仕組みである。
- サブルーチンと変数をまとめる
- クラス内部だけで使う変数やサブルーチンを隠す
- 1つのクラスからインスタンスをたくさん作る
ポリモーフィズム
- ポリモーフィズムは、サブルーチンを呼び出す側のロジックを一本化する
- すなわち
共通メインルーチン
を作る仕組みである
継承
- 継承は、「クラスの共通部分を別クラスにまとめる」仕組みである
- クラス定義の共通部分を別クラスにまとめて、コードの重複を削除する
クラスを型として利用する
- OOPでは、クラスを型として扱うことができる
型チェック
-
機械言語
やアセンブリ言語
の時代には、型チェックの仕組みはほとんどなかった -
高級言語
や構造化言語
では、プログラミング言語が予め用意したデータ型や構造体について、使い方をチェックする仕組みが導入された - OOPではそこからさらに進歩して、変数とメソッドをまとめたクラスを型として定義することにより、プラグラム内のルールとして強制する仕組みが備わった
静的な型付け
- プログラムのコンパイル時点でエラーを検出する方法
- JavaやC#などが採用している
動的な型付け
- プログラムの実行時点でエラーを検出する方法
- Python・Ruby・PHP・Smalltalkなどが採用している
進化したOOPの仕組み
- 現在主流のプログラミング言語であるJava・C#・Python・Ruby・PHPといった言語には、さらに進んだ機能が用意されている
- その代表的なものが、
パッケージ
・例外
・ガベージコレクション
である
パッケージ
- まとめる仕組みとしてクラスがある
- パッケージは、このクラスをさらにまとめる仕組みである
例外
- 例外は、戻り値とは違う形式で、メソッドから特別なエラーを返す仕組み
- 無駄を省くことと、間違いを防止することの2つの効果がある
ガベージコレクション
- ガベージコレクションは、インスタンスの削除処理をシステムが自動的に実行する仕組み
- 不要になったインスタンスでメモリが圧迫される問題を防ぐ
OOPを生かすも殺すも心がけ次第
- 単にクラスやポリモーフィズム、継承の仕組みを使うだけで、保守性や再利用性が向上するわけではない
- 大切なのは、品質が高く捕手や再利用がしやすいプログラムを作るという目的である
- オブジェクト指向は、そのための手段でしかない
- まず、どうすれば保守・再利用がしやすくなるかを考える
- 次に、基本三構造(順次進行・条件分岐・繰り返し)や共通サブルーチンを使って実装することを考える
- そして、それでは物足りないとわかった時こそが、
クラス
・ポリモーフィズム
・継承
の出番である
第5章 メモリの仕組みの理解はプログラマのたしなみ
プログラムの実行方式
- プログラムの実行方式は、大きく分けて
コンパイラ方式
とインタプリタ方式
の2つがある - JavaやC#は、このどちらでもない
中間コード方式
を採用している
コンパイラ方式
- プログラムに書かれた命令を、コンピュータが理解できる機械語に変換してから実行する
- 機械語を直接読み込むため、実行速度が速いことがメリット
- コンパイルが必要なため、実行するまでに時間がかかることがデメリット
- 銀行や政府、企業の基幹システムで採用される
インタプリタ方式
- プログラムの命令を、その場で逐次解釈しながら実行する
- プログラムを書いただけで、すぐに実行して結果を確認できることがメリット
- また同じプログラムを異なる環境(OS・プラットフォーム)で動かすことができるのもメリット
- 実行速度の遅さがデメリット
- Webで採用される
中間コード方式
- コンパイラ方式とインタプリタ方式の良いとこ取り
- まずコンパイラを使って、ソースコードを特定の機械語に依存しない中間コードに変換する
- 次に、その中間コードを専用のインタプリタによって解釈する
- これにより、同じプログラムを異なる環境で速く動かすことができる
スレッド
- スレッドとは、プログラムの実行単位
- プロセスよりも小さな単位で、1つのプロセスの中に複数存在する
- このように、スレッドを複数同時に実行できる環境を
マルチスレッド環境
という - 複数のスレッドを同時並列で処理することで、CPUのリソースを効率よく利用している
メモリ領域
- プラグラムのメモリ領域は、
静的領域
・ヒープ領域
・スタック領域
の3つに分けて管理されている - 3つのメモリ領域の特徴は、以下の通り
|種類|静的領域|ヒープ領域|スタック領域|
|:-----------|:-----------|:-----------|:-----------|
|使い方|アプリケーション開始時に確保する|開始時に一定領域を確保し、必要の都度アプリケーションに割り当てる|LIFO(後入れ先出し方式)|
|格納される情報|グローバル変数、実行コード|任意(アプリケーションによる)|呼び出したサブルーチンの引数、ローカル変数、戻り先|
|確保される単位|アプリケーションでまとめて1つ|システムまたはアプリケーションで1つ|スレッドごとに1つ|
クラス情報は1クラスにつき1つだけロード
- コード情報はクラス固有の情報として、1クラスにつき1つだけメモリにロードする
- クラス情報をメモリにロードするタイミングは2つ
- 事前に全てのクラス情報を一括してロード
- 必要な時点で逐次ロード
- クラス情報をロードするメモリ領域は、静的領域(
メソッドエリア
)である
インスタンス生成のたびに、ヒープ領域が使われる
- 従来のプログラミング言語では、ヒープ領域を使わないのが常識だった
- しかしOOPでは、作成したインスタンスは全てヒープ領域に配置される
- プログラマは、「OOPで書いたプログラムは有限のメモリ領域であるヒープ領域を大量に使って動くこと」を認識しておく必要がある
インスタンスの格納
- 変数には、インスタンスそのものではなく、インスタンスのポインタ(場所を示す情報)が格納される
- インスタンスが格納されている変数に他の変数を代入した場合、このポインタがコピーされるだけ
- ヒープ領域にあるインスタンスそのものは変化しない
メソッドテーブル
- メソッドテーブルは、メソッドエリア(
静的領域
)内において、各クラスに定義されたメソッドがメモリに展開されている場所(すなわちメソッドのポインタ)を順番に格納したもの - メソッドテーブルは、異なるクラスを同じ顔に見せるための「同じ仮面をかぶるための仕掛け」である
継承されたクラスのメモリ配置
- スーパークラスから継承したメソッドとインスタンス変数のメモリ配置は、全く異なる
- スーパークラスから継承したメソッドは、メソッドエリアのメソッドテーブルに格納されるが、実際の情報はスーパークラスのものを使い回す
- スーパークラスのインスタンス変数は、ヒープ領域にあるサブクラスの全てのインスタンスにコピーして保持する
孤立したインスタンスはガベージコレクションが処分
-
ガベージコレクタ
と呼ばれる専用のプログラムが、ガベージコレクションを行う -
ガベージコレクタ
は、どこからも参照されていない孤立したインスタンスを探して削除を行う
第6章 OOPがもたらしたソフトウエアとアイデアの再利用
再利用部品群
- OOPを使ってアプリケーションを開発する場合、毎回全てをゼロから作るのではなく、再利用部品群を使い回して作るのが当たり前になっている
- この再利用部品群には、
クラスライブラリ
・フレームワーク
・コンポーネント
などがある
クラスライブラリ
- クラスライブラリとは、汎用的な機能を持つクラスをたくさん集めたものである
- クラスライブラリはOOPの仕組みを利用することで、以下のことが可能になった
1)ライブラリ中のクラスからインスタンスを作成して、メソッドと変数定義をまとめて利用する(クラスの利用)
2)ライブラリから呼び出される側のロジックをアプリケーション固有の処理で置き換える(ポリモーフィズムの利用)
3)ライブラリ中のクラスに、メソッドや変数を追加定義して新しいクラスを作成する(継承の利用)
フレームワーク
- フレームワークとは、特定のプログラミング言語で書かれた特定の目的を持つ再利用部品群のことである
- クラスライブラリは、OOPの仕組みを利用して作った再利用部品を指すだけで、目的や使い方までは限定しない
- フレームワークは、単にOOPを利用して作ったライブラリというだけでなく、特定の目的を果たすためのアプリケーションの半完成品である
- フレームワークの仕組みの特徴を表すものとして、
ハリウッドの原則
がある
コンポーネント
- コンポーネントの一般的な定義は、以下の通り
・OOPのクラスよりも粒度が大きい
・ソースコード形式ではなく、バイナリ形式で提供される
・コンポーネント定義情報を含めて公開される
・機能の独立性が高く、内部の詳細を知らなくても利用できる
デザインパターン
- 再利用部品群を作った開発者たちは、プログラミング言語や開発環境、ソフトウエアの対象領域が違っていても、共通する設計のアイデアがあると気づいた
- その設計のアイデアを形にして再利用したものが、デザインパターンである
- デザインパターンは、プログラミング言語やアプリケーションの適用分野によらず、様々な場面で繰り返し使われるクラス構造に名称をつけてパターン化したもの
- 1995年に発表された23のパターンは、
GoFのデザインパターン
と呼ばれる
第7章 汎用の整理術に化けたオブジェクト指向
上流工程で集合論と役割分担に応用
- コンピュータは、あくまでも現実世界の仕事の一部を肩代わりするだけである
- そのため、プログラミングに入る前に業務分析や要件定義などの
上流工程
が必要 - この
上流工程
で、オブジェクト指向は集合論
と役割分担
という2つの仕組みを提供する
集合論
- 1つのクラスから実行時にたくさんのインスタンスを作る仕組みが、集合論における集合と要素によく似ている
- そのため、クラスとインスタンスは上流工程では
集合論
に応用されることになった
役割分担
- オブジェクト指向における「メッセージパッシング」は、インスタンスを指定してそのクラスのメソッドを呼び出す
- これは、特定の役割を持つもの同士が決められた方法で連絡し合う、
役割分担
に応用されることになった
汎用の整理術
- この
集合論
と役割分担
は、非常に強力なコンセプトとなった - これによりオブジェクト指向は、物事を分類して整理する仕組みと役割分担を表現する仕組みを備えた
汎用の整理術
となった
2つの側面
- オブジェクト指向には、抽象的な
汎用の整理術
と具体的なプログラミング技術
という、2つの側面がある - 上流工程では
汎用の整理術
、設計以降の下流工程はプログラミング技術
- この2つをしっかり分けて考えることが大切
クラス
- クラスという言葉は、この2つの側面によって意味が異なる
-
汎用の整理術
としては「現実世界に存在するもの」 -
プログラミング技術
では「サブルーチンと変数をまとめる仕組み」
第8章 UMLは形のないソフトウエアを見る道具
- UMLは、「Unified Modeling Language」の略
- 「言語(Language)」とついているが、ソフトウェアの機能や内部構造を表現する図の描き方のこと
- 自然言語とコンピュータ言語の欠点を補うための言語と言える
- 使い方は、大きく3つある
使い方1: プログラム構造や動作を表現
クラス図
- クラスの定義情報とクラス間の関係を表現
シーケンス図
- インスタンス間の相互作用を、時系列で表現
コミュニケーション図
- インスタンス間の相互作用を、インスタンスの関係を中心に表現
使い方2: 汎用の整理術の成果物を表現
クラス図
- 集合論で分類・整理した現実世界の物事の関係を、表現
シーケンス図
- 役割分担された人や組織が強調して全体の仕事を達成する様子を、時系列で表現
コミュニケーション図
- 役割分担された人や組織が強調して全体の仕事を達成する様子を、構造中心に表現
使い方3: 非オブジェクト指向を表現
ユースケース図
- コンピュータに任せる仕事の範囲を表現
アクティビティ図
- 現実世界の仕事の流れを表現
ステートマシン図
- 外部からのイベントによる状態の変化を表現
第9章 現実世界とソフトウエアのギャップを埋めるモデリング
コンピュータの得意技
- まず前提として、コンピュータは「決まり切った仕事」と「覚える仕事」が得意である
業務分析、要件定義、設計でギャップを埋めるモデリング
-
業務分析
・要件定義
・設計
の3ステップにより、現実世界とソフトウエアのギャップを埋めるモデリング
を行う - ここでの
モデリング
は、「UMLを使ってソフトウエアの機能や内部構造を2次元の図で表現すること」を指す
ステップ1: 業務分析
- 現実世界の様子をそのまま捉えて、仕事の進め方を整理する
ステップ2: 要件定義
- コンピュータの「決まり切った仕事と覚える仕事が得意」という性質を考慮して、肩代わりさせる仕事の範囲を決める
ステップ3: 設計
- ハードウエアの能力、OSやミドルウエアの特性、プログラミング言語の表現能力などを考慮して、ソフトウエアをどう作るのかという構造を決める
アプリケーションによって変わるモデリングの内容
- アプリケーションは、
ビジネスアプリケーション
・組み込みソフトウエア
・スタンドアロンアプリケーション
に大きく分類できる - このうち、
ビジネスアプリケーション
と組み込みソフトウエア
について解説する
ビジネスアプリケーション
- ビジネスアプリケーションは、企業などのビジネス活動に利用するソフトウエアである
- ビジネスアプリケーションのモデリングでは、現実の出来事を記録するために、データ構造に現実世界が反映される
- ステップ通りに
業務分析
・要件定義
・設計
を行う
組み込みソフトウエア
- 組み込みソフトウエアは、電気製品や機械の中に組み込まれて動いているソフトウエアである
- 組み込みソフトウエアのモデリングでは、機械を動かして現実世界の仕事をそっくりそのまま置き換えることを目的とする
- 3ステップの
業務分析
は行わず、代わりに現実世界の仕事をそのまま置き換えるための装置の研究開発行う - この研究開発と、ソフトウエアの開発を並行して行う
第10章 擬人化して役割分担させるオブジェクト指向設計
- 保守に強く再利用しやすいソフトウエアの設計の目標は、「重複を排除する」「部品の独立性を高める」「依存関係を循環させない」の3つがある
設計の目標1: 重複を排除する
- 機能が重複すると、その分規模が大きくなって修正漏れが起こりやすい
- そのため、設計の段階で重複が発生しないように配慮しておく必要がある
- 重複排除のために、OOPでは
ポリモーフィズム
や継承
などの仕組みが用意されている
設計の目標2: 部品の独立性を高める
- 1つのソフトウエアは、複数のサブシステムや部品から構成されている
- 保守性や再利用性を高めるためには、このサブシステムや部品の「独立性が高いこと」が重要になる
- 独立性を高めるための考え方として、
凝集度
と結合度
という2つの尺度がある
凝集度
- 個々の部品の機能のまとまり度合いを評価する尺度
-
凝集度
が強いほど、 良い設計と言える
結合度
- 部品間の結びつき度合いを評価する尺度
-
結合度
が弱いほど、良い設計と言える
部品の独立性を高めるコツ
- 独立性を高める具体的なコツは、以下の3つ
- 一言で表現する名前をつける
- 秘密(プライベートメソッドなど)をたくさん作る
- 小さく作る
設計の目標3: 依存関係を循環させない
- ソフトウエアに秩序を持ち込むためには、パッケージやクラスの依存関係を循環させないことが重要である
- 依存関係とは、ある部品Aが別の部品Bを利用している関係のこと
- ここに部品Cも加わり、部品Aは部品Bに依存して、部品Bは部品Cに依存して、部品Cは部品Aに依存している
- という循環をさせないことが重要
第11章 オブジェクト指向から生まれたアジャイル開発
- オブジェクト指向の技術だけでなく、作業手順や成果物を体系的にまとめた開発プロセスも重要である
-
ウォーターフォール型開発プロセス
と反復型開発プロセス
について解説する
ウォーターフォール型開発プロセス
- 要件定義→設計→プログラミング→テストの作業を1回ずつ実施する
- 今よりも開発環境が貧弱だった時代の、「ソフトウエアの変更に大きなコストがかかること」を前提としている
- このプロセスには限界があり、要求の問題と技術の問題の2つに分けられる
要求の問題
- 最初の段階でシステムに要求される機能を全て定義しようとするが、これは上手く行かない
- 後から次々と仕様変更が発生する
技術の問題
- 新しい技術や新製品を導入した際、後半段階のプログラミングやテストで問題に気付くことになる
反復型開発プロセス
- ウォーターフォールの2つの問題を考えられたのが、反復型開発プロセス
- 要件定義→設計→プログラミング→テストを繰り返し行うことによって、段階的にソフトウエアを作っていく
XP(eXtreme Programming)
- 反復型開発プロセスの1つで、
4つの価値
と12のプラクティス
を定義している - XP以前の開発プロセスは「管理者の視点」から見ていたが、XPは「開発メンバーの視点」から見ている
- そのため、現場の開発者たちから大きな支持を受けた
スクラム
- こちらも反復型開発プロセスの1つで、ラグビーのスクラムのようにチームメンバーが力を合わせて仕事を進めることに由来している
- チームに参加するメンバーの役割を、
プロダクトオーナー
・開発チーム
・スクラムマスター
の3つだけに定めている - スクラムは、「チームの視点」「マネジメントの視点」を重視している
アジャイル
- XPやスクラムなどたくさんの反復型開発プロセスを、まとめて
アジャイル開発手法
と呼ばれる - アジャイルの代表的なプロセスとして、
テスト駆動開発(TDD)
・リファクタリング
・継続的インテグレーション(CI)
がある
|プラクティス|内容|
|:-----------|:-----------|
|テスト駆動開発(TDD)|テストコードを先に書いてから本体コードを開発する|
|リファクタリング|完成したプログラムの内部構造を後から安全に改造する|
|継続的インテグレーション(CI)|コンパイル、ビルド、単体テストを定常的に自動実行する|
- アジャイル開発のプラクティスは、OOPには直接関係はない
- しかしTDDやリファクタリングは、OOPの恩恵を深く受けている
第12章 オブジェクト指向を使いこなそう
オブジェクト指向はブームでは終わらない
- オブジェクト指向はソフトウエア開発で欠かせない技術であり、この状況はこれからも続く
- 一時、
アスペクト指向
・エージェント指向
・サービス指向
といった技術が、オブジェクト指向の次に来る技術として注目された - しかし、広く普及しなかったり限定的な利用にとどまっている
- そのため、オブジェクト指向を学ぶことは重要
オブジェクト指向を使いこなす
- オブジェクト指向の利用自体が目的になってはいけない
- レビューの際に、「これはオブジェクト指向らしい設計じゃない」という指摘を繰り返し受けたら要注意