##はじめに
まず、オブジェクト指向についての理解が曖昧で、オブジェクト指向はすごく難しいものだという認識しかもっていませんでした。どういった意図でどのような背景があり、オブジェクト指向が用いられているのかなんて考えたことがありませんでした。もしかしたらそんな方もいらっしゃるかもしれないと思い、そんな方にこの記事を読んでいただきたいという想いからこの記事を書くことにしました。
#目次
1.オブジェクト指向はソフトウェアの開発を楽にする技術
2.オブジェクト指向と現実世界は似て非なるもの
3.OOPを理解する近道はプログラミング言語の歴史にあり
4.OOPは無駄を省いて整理整頓するプログラミング技術
5.メモリの仕組みの理解はプログラマのたしなみ
6.OOPがもたらしたソフトウェアのアイデアと再利用
##1.オブジェクト指向はソフトウェアの開発を楽にする技術
表題通りオブジェクト指向とは、ソフトウェア開発を楽にする総合技術のことです。
オブジェクト指向の台頭により、これまでは不可能だった大規模なソフトウェアの再利用部品群を作ることが可能になったのです。また今では、UML(統一モデリング言語)や、デザインパターン、モデリングや開発プロセスなど、ソフトウェア開発の大部分をカバーしているのがこのオブジェクト指向であり、ソフトウェア開発においては、避けて通れないものになっています。
**しかし、**このような優れた技術であるのも関わらず、オブジェクト指向は難しくとっつきづらいものという印象が根付いてしまっています。その原因として、オブジェクト指向そのもののカバーしている範囲が広く、技術そのもののが複雑であることが考えられるが、**それ以上に状況が混乱していることが最大の要因である筆者は考えています。**そしてこの状況が混乱している原因として、3つの要因があげられます。
- 用語の洪水
- 比喩の乱用
- なんでもオブジェクト症候群
1.用語の洪水
先にも述べたように、オブジェクト指向はソフトウェア開発において、その大部分をカバーしている技術である。そんな中で、多くの人にとってオブジェクト指向との出会いは、下記のような聞き慣れない大量の特殊用語を浴びせられることから始まるのではないでしょうか?
継承、汎用、特化、スーパークラス、サブクラス、インターフェース、多重継承、属性、関連、集約、委譲、オーバーライド、オーバーロード、アクセス制御、コンストラクタ、パッケージ、例外、ガーベージコレクション、フレームワーク、クラスライブラリ、コンポーネント、デザインパターン、ユースケース、モデリング、UML、リファクタリング、アジャイル開発プロセス、RUP、XP・・・
2.比喩の乱用
下記にあるような比喩だけが強烈な印象に残りやすいのです。
「動物がスーパークラスで、哺乳類や魚類がサブクラス。卵を産みミルクで子供を育てるカモノハシは、爬虫類と哺乳類の多重継承に相当する。」
「人は”生年月日”という属性を持つ。具体的な人である田中さんに”年令を教えてください”というメッセージを送ると、”28才です”と答えが帰ってくる。」
3.なんでもオブジェクト症候群
オブジェクト指向は「モノ中心」あるいは、「モノ指向」という意味ですが、これを言葉通りに解釈すると、現実世界の人や組織、物事や出来事、コンピュータシステムの機能、システムが管理する情報など、なんでもオブジェクトであると説明できます。こうした極端な抽象化を筆者は「なんでもオブジェクト症候群」と呼んでおり、これがさらに状況を混乱させています。
2章 オブジェクト指向と現実世界は似て非なるもの
オブジェクト指向はよく現実世界になぞらえて説明されます。そのような抽象的な説明が逆にオブジェクト指向の理解を妨げていることもあります。
オブジェクト指向においては、クラスからインスタンスを作ります。
しかし、現実世界は具体的なものがあり、それを見る側の興味や違いによっていろんな基準で分類します。筆者でいえば、会社に行けば「会社員」で、顧客との打ち合わせ時には「技術者」、家に帰れば「父親」です。
このようにオブジェト指向と現実世界は似て非なる物なのです。
3章 OOPを理解する近道はプログラミング言語の歴史にあり
プログラミング言語は
機械語→アセンブリ言語→高級言語→構造化言語
というように変遷してきました。
この変遷をたどっていくことで、オブジェクト指向の理解に近づくことができます。
それでは順番に見ていきましょう。
機械語
コンピュータは2進数でかかれた機械語しか理解できません。
A10010
8B160210
01D0
A10410
これはごく簡単な算術を実行する命令が書かれているのですが、さっぱりわからないですよね。
当時はこういった機械語を使いこなせる一部のスーパープログラマだけがコンピュータを操れる時代でした。
アセンブリ言語
そこでこうした非効率なプログラミングを改善するためにアセンブリ言語が登場しました。先程の無機質な機械語を人間にわかりやすい記号に変えて表現します。
MOV AX, X
MOV DX, Y
ADD AX, DX
MOV Z, AX
しかし、まだこの段階では専門家でない限り、プログラムの内容を把握することは難しかったのです。
高級言語
そこで次により人間に親しみやすい表現形式でプログラムを書くために高級言語が発明されました。
Z = X + Y
これまでの言語と比べてみれば、高級言語のわかりやすさは明らかですよね。この高級言語の登場によって、プログラミングの生産性や品質は、大きく向上しました。1960年代後半には、ソフトウェア危機が宣言されたほどです。
構造化言語
このソフトウェア危機に対応するために様々なアイデアが提案されました。その中で当時最も注目を集めたのが、構造化プログラミングです。これは、一言でいえば、「正しく動作するためには、わかりやすい構造にすることが重要」という考え方です。
そこで、構造化言語では下記の2つの問題点を解決しました。
- GOTO文の乱用によるスパゲッティコード
- 共通サブルーチンによる再利用
1つ目は、プログラムをわかりにくくしている元凶であるGOTO文を廃止して順次進行、条件分岐、繰り返しの3つの構造だけで表現することを提唱しました。
2つ目は、プログラムを保守に強くするためにサブルーチンの独立性を高めました。サブルーチンの独立性を高めるためには、メインルーチン(呼び出し側)とサブルーチンで共有する情報を少なくすることが重要です。そして、複数のサブルーチンが共有する変数のことをグローバル変数といいます。
このグローバル変数は、プログラム全体のどこからでもアクセスできるため、変数が不正な場合、どこからアクセスされているか調べるために、ソースコード全てを調べなければいけません。そのため、このグローバル変数をいかにして減らせるかが全体の保守性を向上させるのです。これを解決するために、ローカル変数と、引数の値渡しが考案されました。
この構造化言語がプログラマの間では常識になりました。しかし、構造化言語では解決できない2つの問題が残りました。
- グローバル変数
- 貧弱な再利用
1つ目の、グローバル変数に関しては、ローカル変数を考案したものの、サブルーチン呼び出しが終わると消えてしまうため、保持する必要のあるデータはグローバル変数として持たざるを得ませんでした。
2つ目の、貧弱な再利用に関しては、構造化言語で汎用ライブラリが提供されていました。
しかし、増大するアプリケーションの規模全体からすると微々たるものでした。
これらの構造化言語での限界を打ち破るのが、本テーマのオブジェクト指向なのです。
4章 OOPは無駄を省いて整理整頓するプログラミング技術
構造化プログラミングによって、グローバル変数経由での情報の受け渡しを必要最小限に抑えることが可能になりました。しかしローカル変数は、サブルーチンの呼び出しが終わると存在自体がなくなってしまう一時的な変数です。すなわち、グローバル変数として保持せざるを得ない課題があるのです。また構造化プログラミングで再利用できるのは、サブルーチンだけという貧弱なものでした。この2つの課題を解決するために、OOPが登場しました。具体的には、OOPの三大要素(クラス(カプセル化含む)、ポリモーフィズム、継承を利用します。
クラス
クラスは、まとめて、隠して、たくさんつくる仕組みのことです。
-
サブルーチンと変数をまとめる
- 全体の部品の数を減らせる
- 責務が明確かつクラス内で重複しなければ良いため、サブルーチンの名前づけが楽になる(探しやすくなる)
-
クラス内部だけで使う変数やサブルーチンを隠す(
public
やprivate
)- グローバル変数を使わずにすむ
-
1つのクラスからインスタンスをたくさんつくる
- クラスとして定義すると、実行時にいくつでもインスタンスをつくることができるため、同種の情報を複数同時に扱う処理であってもそのクラス内部のロジックをシンプルにできる
インスタンス変数は、長持ちするローカル変数(一旦インスタンス変数が作られたあとは、必要なくなるまでメモリ上に表示される)、仲間内のグローバル変数(別のクラスのメソッドからアクセスできないように隠すことができる)です。さらに、必要な分だけつくることができます。
ポリモーフィズム
サブルーチンを呼び出す側のロジックを一本化する仕組みです。すなわち、共通メインルーチンをつくる仕組みです。共通サブルーチンは呼び出す側が増えても、呼び出される側を修正する必要がなく、共通メインルーチンは呼び出される側が増えても、呼び出す側を修正する必要がありません。
継承
クラス定義の共通部分を別クラスにまとめることで、コードの重複を排除する仕組み。継承されたサブクラスはスーパクラスの変数とサブルーチンを利用できます。
なお、継承を宣言することはポリモーフィズムの利用を宣言することでもあります。
その他
- 型にはめることで楽をできる。メモリ領域を有効に利用できるため。またプログラムのエラーを未然に防げる。
- GOTO文や明示的なポインタの使用、共用体、グローバル変数など不要な機能を使えなくするというある種退化している。
- さらに進化もしている。
- パッケージ(まとめたクラスをさらにまとめる、名前付の法則がある
jp.co.nikkeibp
) - 例外(戻り値とは異なる形式で、メソッドから特別なエラーを返す)
- ガーベージコレクション(不要になったインスタンスをメモリ上から削除する)
- パッケージ(まとめたクラスをさらにまとめる、名前付の法則がある
5章 メモリの仕組みの理解はプログラマのたしなみ
プログラムの実行方式
プログラムの実行方式は3種類あります。
コンパイラ方式(コードを機械語にコンパイラで一括変換)
- 実行効率が良い
- 実行するまで手間
- 実行したあとにエラーが判明
インタプリタ方式(コードを機械語に逐次変換)
- 手軽に実行できる
- 異なるマシンやOSで互換性を保てる
- 実行速度が遅い
中間コード方式(2つの良いところ取り)
- 実行効率が良い
- 異なるマシンやOSで互換性を保てる
スレッド
スレッドとは、プログラムの実行単位(プロセスよりも小さい)のことです。スレッドは、プロセスよりも小さな単位で、1つのプロセスの中に複数存在することが可能です。スレッドはマルチスレッド環境であり、無駄な待ち時間を減らして、全体の実行効率をあげています。
メモリの使い方
C言語においてメモリは典型的に、5つのセグメントにわけられていますが、今回はその中でも3つのセグメントに着目しています。自分自身メモリに関しては、C言語でかじった程度ですが、今回オブジェクト指向のメモリの使い方を見ていく中でまた、一段と理解が深まりました。というわけでメモリってなんぞやって方のためにもそれぞれのセグメントを見ていきましょう。
静的領域(本書では、メソッドエリア)
静的とは、あらかじめきめられたという意味であり、外部変数(グローバル変数)や静的変数(static変数)などの、プログラム実行中に変化しない変数が格納される場所です。この静的領域の大きさはプログラム実行時に確保され、プログラム実行中にサイズが変化することがなく、固定サイズです。グローバル変数が格納されるので、スタックメモリに配置されるローカル変数と異なり、プログラム動作中にラベルが破棄されることがないため、特定の関数呼び出しに依存せず値を保持し続けることができます。
ヒープ領域
ヒープは通常動的なメモリ割り当てが行われるセグメントです。ヒープ領域はmalloc、realloc、freeによって管理され、基本的には、プログラマ側が管理する必要があります。しかし、オブジェクト指向においてはガベレージコレクションが存在するため、管理する必要がない場合もあります。
スタック領域
スタック領域はスレッドの制御のために使うメモリです。複数のスレッドから共用されるヒープ領域と比較して、このスタック領域は、スレッドに一つずつ用意されています。このメモリ領域をスタックと呼ぶのは、このスタック領域がLIFOと呼ばれる使い方をするからです。これは、新しい情報がどんどん積み重ねられ、使うときは一番上から使う方式です。サブルーチン呼び出しは、呼び出したサブルーチンの処理が終了する前に、さらに次にサブルーチンを呼び出す入れ子構造になるため、この方式で効率的にメモリを使用することが可能です。
###オブジェクト指向におけるメモリの使い方
- クラス情報はクラスにつき1つだけロードされる
- OOPで書かれたプログラムは、有限のメモリ領域であるヒープ領域をじゃぶじゃぶ使って動く
- インスタンスを格納する変数にはインスタンスそのものではなく、インスタンスのポインタ(場所を示す情報)が格納される
- インスタンスを格納する変数を他の変数に代入した場合、ポインタがコピーされるだけで、ヒープ領域にあるインスタンスそのものは変化しない
- ポリモーフィズムは異なるクラスが同じ顔を見せる
→共通のメインルーチンを使用するために、メソッドテーブルを使用する。メソッドテーブルは、各クラスのメソッドのポインタを格納してるもの
- 継承される情報の種類によってメモリ配置は異なる
- 孤立したインスタンスはガベージコレクタが処分する
6章 OOPがもたらしたソフトウェアとアイデアの再利用
1.最初にOOPを利用して再利用部品群をつくり
2.次に再利用部品群に共通して現れる設計のアイデアを抽出したデザインパターンが登場し、
3.最後は反対に再利用部品群を作るためにデザインパターンを利用するようになった。2と3が循環して、相互に発展していった。
再利用部品群
クラスライブラリ
クラスライブラリとは、汎用的な機能を持つクラスをたくさん集めたものです。また、 クラスライブラリは言語仕様の一部であり、OOPを使ってプログラミングをする上でとても重要な物です。従来のプログラミング言語では、関数ライブラリ(再利用できる部品はサブルーチンだけ)でした。しかし、OOPには、クラス、ポリモーフィズム、継承という仕組みが備わっていたため、たんに呼び出すだけでなく、次のことが可能になりました。
- (クラスの利用)ライブラリ中のクラスからインスタンスを作成して、メソッドと変数定義をまとめて利用する
- (ポリモーフィズムの利用)ライブラリから呼び出される側のロジックをアプリケーション固有の処理で置き換える
- (継承の利用)ライブラリ中のクラスに、メソッドや変数を追加定義して新しいクラスを作成する
フレームワーク
クラスライブラリと似ているが、特定の目的を果たすためのアプリケーションの半完成品のことです。基本的な制御の流れをあらかじめ用意し、アプリケーションで個別の処理を組み込むようにしたソフトウェア部品群であり、基本的な処理はフレームワーク側で用意して、アプリケーション固有の処理(継承を利用してあらかじめ用意)は、ポリモーフィズムを利用して呼び出す仕組みになっています。これはいわゆる、ハリウッドの法則(Don't call us, we will call you.)と呼ばれています。ハリウッドでの、「必要なときはこちらから連絡するから、売り込みの電話はするな」という制作側の言葉を電話のcallとメソッドの呼び出しのcallでかけたものです。
コンポーネント
コンポーネントは、OOPのクラスよりも粒度が大きく、ソースコード形式ではなく、バイナリ形式として提供されます。
また、コンポーネントの定義情報を含めて提供され、他にも機能的に独立性が高く、内部の詳細を知らなくても利用できるといった特徴を持っています。
デザインパターン
デザインパターンとは、設計の定石集のことです。デザインパターンはクラスライブラリやフレームワークの開発経験を通じて作られたものですが、今では反対にクラスライブラリや、フレームワークで利用されています。代表的なもので、GoFの23デザインパターンというものがあります。詳しくは、リンクをご覧ください。
##おわりに
さて、いかがだったでしょうか?参考書籍に関しては、まだ半分のところまでの内容でしたが、僕自身はオブジェクト指向に対する考え方がガラッと変わりました。これほどまでに先人達の知恵が詰まっていて、プログラミング言語の歴史を変える技術であり、使いこなせればソフトウェア開発において、保守性や可読性などの大幅な向上が計れるすばらしい技術であると認識を改めることができました。
長くなりましたが、読んでくださりましてありがとうございます。