PR

店長クラスから店長オブジェクトを作る

 せっかくコードを書いても,何も実行結果が得られないのでは面白くありません。ここで少し試してみましょう。クラスという設計書から,具体的なオブジェクトを作ります。それにはnewというキーワードを使います。また,オブジェクトにも名前を付けます。例えば,

chief Bob = new chief();

は,Bobという名前のオブジェクトをchiefクラスから作るコードです。Bobという名前は筆者が勝手に付けたもので,別にCathyでもBillでも構いません。

 chiefクラスのメソッドを呼び出すには,オブジェクト名の後にピリオドを付けて,メソッド名を記します。具体的には,

Bob.sayHello();
Bob.sayThankyou();

などとします。「ボブ,あいさつしろよ」「ボブ,お礼を言えよ」といったところでしょうか。

リスト2●店長(chief )オブジェクトを作って,メソッドを実行するプロ グラム(shopping クラス)のコード
図5●リスト1,2の実行結果
リスト3●店員(clerk )クラスを,基本クラスとして書き直したコード。
protected は,クラス内もしくは継承先のクラスからしかアクセスできな いようにするキーワード
リスト4 ●店長(chief )クラスのコード。
getPrice メソッドを具体的に 記述している
リスト5 ●現在時刻に応じて異なる店員(currentClerk )オブジェクトを 作るコード
リスト6●リスト2 を現在時刻に応じて店員が変わるように変更したプロ グラム
図6●リスト6を実行した結果。
現在時刻によって(a)奥さん,(b)アルバイト,(c)店長の3人でキュウリの価格が違う
リスト7●現在時刻で店員オブジェクトを変更する八百屋(greengro- cery)クラス
リスト8●八百屋クラスを利用した買い物クラス

 以上3行のコードが,アプリケーションのエントリ・ポイント(実行開始点)となるように,Mainメソッドを作ります(リスト2[拡大表示])。クラス名は(買い物)shoppingとしました。このshoppingクラスは,すでに記述した店員(clerk)クラスや店長(chief)クラスなどのコードの前に挿入しても,後ろに追加しても,実行結果に差はありません*6。ただし,usingキーワードでSystem名前空間を利用できるようにしておくことを忘れないようにしてください*7

 こうして作ったテキスト・ファイルに,“test.cs”と名前を付けて保存します。これをコンパイルする方法は2通りあります。一つは前回紹介したように,.NET Framework SDKに含まれるコンパイラ(csc.exe)を使う方法*8。もう一つはVisual Studio .Net(VS.NET)を使う方法です。

 今回のプログラムは,特にWindowsを使う必要がないので,コンソール・アプリケーションとしてコンパイルします。VS.NETでコンソール・アプリケーションを作るには,VS.NETの起動後に「新しいプロジェクト」ボタンをクリックして,「新しいプロジェクト」ダイアログボックスの「プロジェクトの種類」で「Visual C#プロジェクト」を選び,「テンプレート」選択肢の中から「コンソールアプリケーション」を選びます。

 すると自動的に“Class1.cs”というファイルが作成されます。この中の自動的に記述されたコードをすべて削除して,リスト1とリスト2の内容を記述し,“test.cs”として保存します。後は「ビルド」メニューから「ソリューションのビルド」を選択して,コンパイルするだけです。実行ファイルは,binフォルダ(VS.NET起動後の設定時に指定したアプリケーションの保存場所のフォルダ内にある)のDebugかReleaseフォルダの中にあります*9。実行するには,コマンドプロンプト上で,実行ファイルのパスを入力します。正しく実行できれば,画面上に「いらっしゃい」と「ありがとうございました」が表示されるはずです(図5[拡大表示])。

店員クラスを基本クラスにする

 ここまでで,店員の持つ三つの性質のうち,sayHelloメソッドと,sayThankyouメソッドの二つが完成しました。残るは,3人の店員でそれぞれ違ったキュウリの値段を答えるgetPriceメソッドです。

 このメソッドには,キュウリの値段を戻す役割があります。そこで,どのような種類の数字を使うのか,あらかじめ指定しておきます。キュウリの値段は整数値(単位:円)ですから,整数値であることを宣言するintキーワードを使えばいいでしょう。したがってgetPriceメソッドもこれまでに使ったvoidの代わりにintを使います。

 ここで注意してほしいのが,getPriceメソッドの中身は店員によって異なるので,店員(clerk)クラスの中では処理内容を書かないという点です。プログラムの中ではリスト2に書いたように,店長(chief)クラスや奥さん(wife)クラスからオブジェクトを作ります。店員(clerk)クラスをそのまま使うことはありません。getPriceメソッドの中身がない(キュウリの値段を答えられない)店員に店番をさせるわけにはいかないからです。

 このように,他のクラスに継承させることを目的としたクラスを「基本クラス」と呼びます。基本クラスであることを示すにはclassキーワードの前にabstractキーワードを置きます。getPriceメソッドのように,継承先のクラスで具体的な処理内容を記述して,基本クラス内ではメソッドの存在だけを定義する場合にも同様にabstractキーワードを使います。加えて,この八百屋にはキュウリの標準価格(standardPrice)があります。これはどの店員に対しても同じ価格なので,店員(clerk)クラスで定義しておきましょう。以上のことから,店員(clerk)クラスはリスト3[拡大表示]のようになります。

 一方,店員(clerk)クラスを継承した店長(chief)や奥さん(wife)クラスでは,getPriceメソッドの具体的な処理内容を記述します。この場合,継承元のメソッドを継承先の処理内容で上書きすることになるので,このことを示すoverrideというキーワードが必要になります。こうしたことから店長(chief)クラスはリスト4のようになります。

 リスト4[拡大表示]にはreturnというキーワードがありますね。これは,その後に書かれた内容を,メソッドの呼び出し元に返すという働きをします。つまり店長(chief)クラスのgetPriceメソッドでは,標準価格の9割を計算して,計算結果を標準出力に表示しつつ,呼び出し元に返す,という処理をすることになります*10。奥さん(wife)クラスは標準価格を1.1倍,アルバイト(parttimer)クラスは,何も計算せずに標準価格をそのまま使います。なお,計算結果はPriceという整数型の変数に代入しています*11

 ちなみに,もしこの八百屋がフランチャイズ展開することになって,フランチャイザの社長が独自の値付けをすることになっても,社長(president)クラスを店員(clerk)クラスから継承して作り,getPriceメソッドを社長独自の計算方法で上書きするだけでプログラムは対応できます。肝心なのは,このとき,店長や奥さんなど他の店員の処理内容を変更する必要がまったくないということです。変更はpresidentクラスだけで済みます。このようにメンテナンスの手間を可能な限り抑えることができるのが,オブジェクト指向プログラミングのメリットです。

現在の時刻にふさわしい店員オブジェクトを作る

 次に,最もオブジェクト指向らしい機能を説明します。これが使えるようになれば,オブジェクト指向プログラミングの基礎をほぼ理解したと言っても過言ではありません。

 コードを使って具体的に説明しましょう。店員(clerk)クラスを継承して作成した店長(chief)クラス,奥さん(wife)クラス,アルバイト(parttimer)クラスは,それぞれの継承元である店員(clerk)としてオブジェクトを作ることができます。例えば現在の店員(currentClerk)がアルバイトであるようなオブジェクトを作成するなら,

clerk currentClerk = new parttimer();

と書くことができます。

parttimer currentClerk = new parttimer();

と書かないところがミソです。

 それでは,新たに買い物(shopping)クラスを作ります。買い物の処理がアプリケーションの実行開始点になるようにMainメソッドに書き込みます。先に作ったMainメソッドが残っている場合は,内容を削除しておいてください。

 買い物の処理の最初は,八百屋に行くことでした。八百屋には店員がいます。ただし時間によってオブジェクト化される店員の種類が違ってきます。時間によって異なるclerkオブジェクトを作成する準備として,まず現在時刻を取得する方法を説明しましょう。現在時刻を取得するには,System名前空間にあるDateTimeクラスのNowプロパティを使います。つまり,下記のように,取得した現在時刻のHourプロパティを取得すれば,時間部分だけを整数値で取得できます*12

int hourofNow = DateTime.Now.Hour;

 これを使って条件分岐のコードを書き,現在時刻にふさわしい店員(clerk)オブジェクトを作ります。条件分岐にはifステートメントを使います。ifステートメントは,これに続くカッコ内に条件を書き,その後に条件が成立したときに実行する処理内容を{ }でくくって記します。例えば,午前中は奥さんが店員になるというコードなら

if(hourofNow < 12) {
clerk currentClerk = new wife()
}

のように書けばいいでしょう。条件が成り立たなかった場合に,他の条件が成立しないかを調べて特定の処理を行うには,elseキーワードを組み合わせます。したがって現在時刻にふさわしい店員(clerk)オブジェクトを作成するにはリスト5[拡大表示]のように書けばいいでしょう。

店員が誰であろうと同じように買い物する

 さて,キュウリの買い物をシミュレーションするには,sayHelloメソッド,getPriceメソッド,さらにその値段が100円以下で購入したときにはsayThankyouメソッドを実行することになります。

 先にcurrentClerkという名前の店員(clerk)オブジェクトを作成したので,このcurrentClerkに対して,それぞれのメソッドを実行します。なお,getPriceメソッドで取得したキュウリの値段を取得したら,100円と比較して買うかどうかを決めます。100円以下ならあなたは買うでしょうから,店員オブジェクトには「ありがとうございました」つまりsayThankyouメソッドを指示します。したがってshoppingクラスのコードはリスト6[拡大表示]のように書けるでしょう。先ほどと同様にコンパイルして実行してみてください(図6[拡大表示])。

 リスト6を見ると,(購買の判断において)店員が実際には誰であるのか全く意識していないことに気が付きましたか? 店員は常にcurrentClerkです。しかし,午前中に実行すれば,キュウリを買い損ねますし,午後に実行すれば(店員がアルバイトなのか店長なのかは判別できませんが)キュウリを購入できます*13

 このように,対応する相手が店長(chief)だろうと奥さん(wife)だろうとアルバイト(parttimer)だろうと,店員(clerk)を継承して作られたオブジェクトであれば,店員(clerk)が持つメソッドをまったく同じように使うことができます。つまり,誰がcurrentClerkになっていようが,呼び出す側の処理は変更する必要がないのです。さらには,3人のそれぞれの処理内容が変更されても,Mainメソッドの中身を変更する必要はありません。このように,オブジェクトを同じように扱いながら,様々に異なる処理をさせられることを「ポリモーフィズム」といいます*14

買い物は店の事情と関係ない

 だいぶオブジェクト指向について明るく見えるようになってきました。でも,まだ問題が残っています。買い物(shopping)クラスが,本来知るはずのない,八百屋の人員ローテーションを知ってしまっている点です。これはリスト6で言えば,(1)の部分です。何時に誰が店番をしているかなんて,お客は知りませんよね。

 そこでアイデアとして出てくるのが,新たに八百屋(greengrocery)クラスを作ることです。すでに作ったshoppingクラス内では,currentClerkに対してgetPriceメソッドを実行し,整数値を取得しました。実はメソッドを実行して取得できるのは,整数値などの数値だけではありません。任意のオブジェクトを取得することもできます。

 つまり,八百屋(greengrocery)クラスを作り,そこから時間に応じて適当な店員を選べば,shoppingクラスが八百屋の都合を知っている必要はなくなります。さっそく八百屋(greengrocery)クラスにsayHelloメソッドを実装し,戻り値を店員(clerk)オブジェクト(オブジェクト名はcurrentClerk)にしてみましょう(リスト7[拡大表示])。そのうえでリスト6を修正するとリスト8[拡大表示]のようになります。

 プログラムが,かなりすっきりとしたものになったのがわかるでしょうか。これだと,買い物客側のロジックと,八百屋側のロジックが完全に切り離されます。店員を増やしたり,担当する時間を変更しても,買い物する側の処理にはまったく手をつけなくてすみます。また,客が増え,プログラム内の様々なところでshoppingクラスを使うことになったり,買い物の処理に変更があっても,八百屋(greengrocery)クラスに手を加える必要はありません。

 これは,「継承」「ポリモーフィズム」に続く,オブジェクト指向技術の三つ目の要素「カプセル化」に結びつきます。カプセル化とは,情報とその情報を使う処理をまとめることです。さらに通常,これらを外部から見えなくします(情報隠ぺい)*15

 今回の例では,店員がキュウリの値段を決めるとき,標準価格というものが存在して,それに個々の店員が特定の倍率を乗じていますが,こうした処理はお客側から意識することはありません。greengroceryクラスを例にとると,現在時刻を引数などで外部から与えなくても,適切な店員を選択します。オブジェクト内に必要な情報(現在時刻,選択する店員のリスト,店員のローテーション)とそれを処理するロジックが内在しているからです。

☆               ☆               ☆

 さて,オブジェクト指向プログラミングのメリットが少しはご理解いただけたでしょうか。まだ説明し切れていないこともありますが,ここから先は皆さんの力で勉強されるといいでしょう。機能やメリットがわかっても,まだ自由に使えるようになったわけではありません。よりオブジェクト指向らしいコーディングを心掛け,腕を磨いてください。