OOPのための設計

 「さあプログラミングするぞ~」と言って,いきなりプログラムを打ち込み始める人などいないでしょう。プログラミングの前に,必ず何らかの設計をするはずです。OOPのための設計のことを「オブジェクト指向設計」と呼びます。

 オブジェクト指向設計に限らず,設計とはすなわち「細分化」のことだと言えます。大規模なプログラムを大規模なままプログラミングすることはできません。設計段階で,小さな要素に細分化し,要素単位でプログラミングし,要素を結合してプログラムを完成させます。オブジェクト指向設計では,「物」に注目した細分化を行います。コンピュータ上に置き換える現実世界の業務や遊びが,どのような物から構成されているかを考えるのです。

図3●オブジェクト指向設計では物に注目して細分化する
図4●電卓アプリケーションの外観
リスト3●Java で記述したボタン・クラス
リスト4●ボタン・クラスの実体を作成する
図5●C言語で記述したプログラムの構造と流れ
図6●Javaで記述したプログラムの構造と流れ

 物のことを英語で「オブジェクト(object)」と呼びます。だから,このような開発手法を「オブジェクト指向(Object Oriented)」と呼ぶのです。プログラミングでは,細分化された物がクラスになります。したがってオブジェクト指向設計の最初のステップは,プログラムに必要とされるクラスを洗い出すことです(図3[拡大表示])。

 クラスを洗い出したら,個々のクラスごとにメンバー(クラスにグループ化される関数と変数)を取り決めて行きます。この順序は,とても重要です。バラバラに洗い出された関数と変数を,後からクラスにグループ化するのではありません。最初にクラスで,後からメンバーです。この順序を守ることで,必然的に大規模なプログラムがグループに整理されます。クラスに分けて,メンバーを決める設計作業のことを「モデリング(modeling)」と呼びます。現実世界をコンピュータ上に置き換えてモデルを作るからです。

 クラスを洗い出す作業には経験が必要ですが,だれにでもできる簡単なコツがあります。それは,プログラムの機能要求仕様(プログラムに要求される機能を表した文書)の中にある「名詞」を洗い出すことです。名詞が,クラスの候補になります。名詞は,物の名前を表すものだからです。ただし,すべての名詞がクラスになるわけではありません。ほかのクラスの中に含まれる変数となる名詞もあります。例えば,以下に示した電卓アプリケーションのシンプルな機能要求仕様の中から名詞を抽出してみてください。

【電卓アプリケーションの機能要求仕様】
ボタン操作で四則演算を行え,液晶画面に8けたで計算結果を表示する電卓

 「ボタン」「四則演算」「液晶画面」「計算結果」「電卓」といった名詞が,クラスの候補になります。現実世界は,物を単位としてグループ化されています。それをそのままプログラムにモデリングできるのがOOPです。物に注目した細分化によって,再利用性と保守性が向上します。なぜでしょうか? それは,同じ物がほかの業務や遊びで使われる場合があり得るからです。プログラムの保守は,現実世界の変化に伴って要求されることだからです。変化した物に合わせて対応するクラスを保守すればよいのです。くどいようですが,再利用性と保守性が向上するようなクラス分けができていてこそです。

【ここまでのまとめ】
・オブジェクト指向設計では,大きなシステムを物に注目して細分化する。
・プログラムの機能要求仕様の中にある名詞がクラスの候補になる。

クラスとオブジェクトの違い

 先ほど「物(オブジェクト)=クラス」と説明しましたが,ちょっとだけ補足しなければならないことがあります。JavaやC++など現在主流のほとんどのOOP言語では,オブジェクトとクラスを異なる概念としています。「クラスはオブジェクトの定義」であり,「クラスの実体がオブジェクト」なのです*3。何のこっちゃ?という感じでしょう。筆者の知り合いのC言語バリバリのプログラマも,OOP言語のこの部分がピンと来ないと言って嘆いていました。

 具体例で説明しましょう。図4をご覧ください。これは,Windowsに添付された電卓アプリケーションの外観です。ウィンドウの上に27個のボタンがありますね。27個のボタンは,27個のオブジェクトです。それでは,27個のボタンは,27個のクラスとしてプログラミングされるのでしょうか? 答えはNOです。それでは,あまりにも面倒だからです。

 ほとんどのOOP言語では,ボタン・クラスを一つだけ定義し,ボタン・クラスの実体を27個作るという手法を使うようになっています。その方が効率的だからです。ボタンの大きさ,位置,色,表面の文字などの違いは,ボタン・クラスの変数に設定できるようにします。ボタン・クラス(MyButton)をJavaで記述するとリスト3[拡大表示]のようになります。

 MyButtonクラスを記述してプログラムを実行しても,画面にボタンは表示されません。なぜなら,MyButtonクラスは,オブジェクトの定義であって実体ではないからです。ボタンを表示したいなら,ボタン・クラスの実体を作成する命令を実行しなければなりません。これがOOP言語の約束事です。Javaでは,MyButtonクラスをデータ型とした変数を宣言し,=で結ばれた右辺にnew MyButton( ) と記述することで,クラスの実体がメモリー上に作成されます(リスト4[拡大表示])。これが,オブジェクトです。オブジェクトは実体があるので,画面に表示されます。こうしたオブジェクトのことを「クラスのインスタンス(instance,実体)」と呼ぶ場合もあります。

 Javaでは,基本的に一つのクラスを一つのプログラム・ファイルとして作成します。このファイルがクラスだと考えてください。プログラム・ファイルのコピーがメモリー上に作成されたものがオブジェクトです。この手法は,現実世界の業務や遊びによく合います。例えば,業務アプリケーションを作成する際に,従業員を表すMyEmployeeクラスを作ったとしましょう。もしも「クラス=オブジェクト」だったら,プログラムの中に従業員オブジェクトは一人ぶんしか存在できなくなってしまいます。それでは,プログラムが役に立ちませんね。実際には「クラス=オブジェクトの定義」なので,必要な数だけMyEmployeeクラスのコピーをメモリーに作成できます。つまり必要な数だけ従業員オブジェクトが存在できるのです。

【ここまでのまとめ】
・ほとんどのOOP言語では,クラスとオブジェクトを異なる概念としている。
・クラスのコピーをメモリーに作成したものが,オブジェクトである。

OOP言語のプログラムの構造と流れ

 クラスとオブジェクトの違いがわかったなら,最後のトピックとして,OOP言語で作成されたプログラムの構造と流れを知っていただきましょう。プログラムの構造は,もうおわかりですね。OOP言語のプログラムは,複数のクラスから構成されます。個々のクラスの中に関数と変数がグループ化されています。

 OOP言語のプログラムの流れは,ちょっと難しいかもしれません。OOP言語でも非OOP言語でも,プログラムには必ず実行開始位置となる部分があります。非OOP言語では,実行開始位置を起点としてさまざまな関数が呼び出されることでプログラムが流れて行きます。OOP言語の場合も関数が呼び出されることでプログラムが流れて行くことは同じですが,関数を呼び出す前に,関数を持っているクラスのオブジェクトをメモリー上に作成しなければなりません。ちょっと面倒なことですが,仕方がありません。

 非OOP言語のC言語(図5[拡大表示])も,OOP言語のJava(図6[拡大表示])も,mainという関数がプログラムの実行開始位置になります。C言語の場合は,mainの中で何らかの関数を呼び出し,その関数の中で別の関数を呼び出す…。このように関数の呼び出しを繰り返してプログラムが流れて行きます。Javaの場合は,mainの中で何らかのクラスのオブジェクトを作成してその関数(メソッド)を呼び出す,呼び出されたメソッドの中でほかのクラスのオブジェクトを作成してそのメソッドを呼び出す…。このようにオブジェクトの作成とメソッドの呼び出しを繰り返してプログラムが流れて行きます。

 OOP言語では,オブジェクトがほかのオブジェクトの関数を呼び出すことでプログラムが動作すると考えるのです。これを「オブジェクト間のメッセージ・パッシング(message passing)」と呼びます。OOP言語のプログラムは,現実世界で物と物が対話している様子をコンピュータ上に置き換えているのです。なかなかカッコイイでしょう!

【ここまでのまとめ】
・OOP言語のプログラムは,複数のクラスの定義から構成される。
・OOP言語のプログラムは,オブジェクト間のメッセージ・パッシングによって動作する。

おわりに

 皆さん,いかがでしたか?この連載の目的は,OOPを理解するための基本的な考え方を伝えることです。今回の記事をお読みいただいたことで,今までOOPに対してモヤモヤしていた暗雲が晴れて「見えてきたぜ! ヨシッ!」となったら,すぐにJavaや .NET対応言語を使ってOOPを実践してください。プログラミングは,実践することでのみマスターできるものだからです。まだまだ実践する自信がないなら,引き続き第2回,第3回の連載をお読みください。

 次回は,OOPの三本柱と言われている「継承」「カプセル化」「多態性(ポリモーフィズム)」を説明します。どうぞお楽しみに!!