kr_ryo 徒然日誌 <2005年8月14日分>

三國志製作記110〜オブジェクトを作るよどこまでも〜

いよいよ来週は我が〔日曜プログラマーの部屋〕 kr_ryo's HomePageの4周年特別記念日を迎えます(^O^)SWG完成に伴い設立したのがこのサイトの由来ですので、4周年がそのまま三國志製作記の積み重ねを意味してしまうんですよね。まさに、めでたくもあり、めでたくもなし…(^^;A

ところが予想外(!?)に三國志製作は進んでおります(^^;;;ようやく緒についたオブジェクト、使えば使うほどなるほどおもしろい発想、という気がしてきます(^-^)なんというか、頭が楽になるんです。作りやすい、といった方がいいかもしれませんね。

どういうことかというと、要するに流れを覚えておかなくてよいので、記憶力を使わないんです。これまでの手続指向型プログラムでは、プログラム上分岐はあったとしても、プログラムはひたすら一本のプログラムです。流れをとぎらせないよう、かつ、様々なことをさせるよう、分岐を作りつつ、その分岐同士の整合を取ったり、また戻っていくよう流れを覚えておかないといけません。これが、大変(*_*)

規模が大きなプログラムだとか、シミュレーションゲームなど展開やできることがいろいろ分かれるゲームだとかでは、用意すべき流れ自体は一本道なわけがなく、途中で様々に枝分かれします。枝分かれすれば枝分かれを作らねばならず、また舞い戻ってこないとならず、枝分かれ部分だけでなく先にも進まねばならず、枝分かれ部分がちゃんとできているかどうかのテストを全部行っていくためにも、枝分かれ部分のそれぞれも作らねばならず……やらないといけないことは無数にあります。先に思いを馳せれば、組み合わせ爆発に暗澹たる気分になることもしばしば…(T-T)

ところが、オブジェクト指向ではじめて(^^;;プログラムを組んでみると、なんとも不思議な感覚に陥ります。流れがないんです。いやもちろん、オブジェクトの中での流れはあります。しかし、最初から最後まで続く一本道と、一本道を構成する枝分かれ(^^;;がいらないのです。逆に、どうやって動いていくの?と不思議な気分に落ちます。

もちろんそれは、ユーザのコマンド、ボタンを押したりメニューを選択したりすること、すなわちイベントによってオブジェクトとその中にある流れを動かしていくんですけども、オブジェクトがオブジェクトを動かしていくこともあります。ところが動かすオブジェクトは、動かされるオブジェクトのことをあんまり知っていなくてよいのです。ボタンを押すユーザが、ボタンを押したことによるプログラム上の様々な動きを知らなくても、ボタンを押したことによる結果さえわかっていればいいように、オブジェクトを動かすオブジェクトも、動かしたことによる結果を知っているだけで済むのです。

これが手続指向型プログラムでは、当然ルーチン(流れ)が分岐のためのサブルーチン(副の流れ)を呼ぶとしても、同じひとつの流れであるため、ルーチンはサブルーチンがどういうものか知っておかねばなりません……というより、単に流れが枝分かれしているだけです。結果だけもらうという流れにできないこともありませんが、そうであったとしても当然サブルーチンもメインルーチンも、お互いどういうものか知っている必要があります。

それがオブジェクト指向だと、別のオブジェクトに仕事を任せたら、結果だけもらうことになります。どういうやり方で別のオブジェクトが仕事をするかはまったく知らなくてもいいのです。その別のオブジェクトが、武将データベースから武力データを探して拾ってくるのか、武将オブジェクトから該当者オブジェクトを探して武力データをもらってくるのか、知らなくてもいいのです。つまり、お互い渡される仕事と結果だけでつながっているだけで、どうやっているのかは知らなくていいのです。これはプログラマの記憶力負担を非常に下げてくれるのです(^-^)あああの流れならこう、この流れならそう、というんでなく、この仕事ならこの結果、この仕事ならこの結果、というだけで済むのです。

結果を返されたオブジェクトも、また、どこかに返すのか、ユーザに結果を表示するのかはプログラムによります。けれどもそれでそのオブジェクトも仕事は完結です。プログラムの最初から最後までの一本道の一部、ではなく、完結に至る過程が短いのです。一本道の流れの一部を切り出して無理やりテスト用のルーチンを作るんではなく、ぺたっとはりつけたボタンを起動イベントにして、あるオブジェクトの動きをテストすることもできます。テスト自体も非常に楽ですね(^-^)

さらに、これは当然最初のオブジェクト指向設計の出来不出来にもよりますが、仕事を渡す相手を覚えておく必要も非常に下がります。どういうことかというと、たとえばある武将の武力データを表示したいとするとします。手続指向型プログラムでは、武力表示コマンドボタンを押されると、どの武将かを選択させるUI(ユーザーインターフェース)を表示させ、ユーザの選択によって、武将データベースから該当武将を探し、見つかったら武力データを取得し、画面に表示させる、という流れになると思います。これだけで済めば手続指向型プログラムの方が簡単なんですけど、見つからなかったらどうするのかとか、どの武将か選ぶのに自勢力の武将だけを選ぶとか、実は調略のためなので他の勢力の武将を選ぶとか、ここだけでもはや枝分かれのタネはいろいろありそうです。

しかも、これだけでもそれなりに大きなプログラムになると思いますけど、シーンシーンで武力データを表示させるために、まったく同じようなプログラムをちょっとずつ変えて毎回書いていたら、いい加減いい加減にしろ、とか思いたくなりますよね(~_~;)戦闘シーン用武力データ表示、新君主選択用武力データ表示とか、同じ部分は同じものとして毎回使いたい、と。実はそれがサブルーチンの本質です。たとえば、武将データベースの部分とか、ユーザに選択させる部分とかは、汎用のものを作りたいな、という気にすぐなります。表示だって、できれば同じものにしたいな、と。

そこでサブルーチンを別に作り、メインルーチンでサブルーチンを呼び出す仕様にするわけですね。で、個別用に作りつつも、汎用をめざして、どれだけそのサブルーチンが再利用できるものになるかがカギになります。それは、できるだけサブルーチンが、仕事を渡して結果だけを返すようにする、という作りです。ん、これはまるでオブジェクト指向そのものじゃあないですか!(@_@)

ところが、サブルーチンはあくまでサブのルーチンであって、最初に作ったメインルーチンの流れを十二分に引っ張っている可能性が非常にあります。たとえば武将選択用サブルーチン。最初は自勢力の武将の武力データを見れればよい、と考えているので、表示させる武将は自勢力であることが当然の前提、ということになります。ところがこのサブルーチンは当然非常に便利なので、また作るのが面倒になって、他勢力の武将調略用にも使いたくなりますが、このままではそれができません。そこでサブルーチンの仕様変更を行い、どの勢力の武将かもサブルーチンに情報を渡すように変えたとします。

当然そうなると、初めに作った部分、自勢力武将の武力データ表示用ルーチンの流れで、サブルーチンに渡ったとたん、必要な情報が不足しているというエラーが出ます。こういう手続指向型プログラムであっても、作りによってはDelphiでは動かす直前にエラーが出てわかるんですけども、残念ながらHSPはじめ、動かしてみないとわからない、という場合もあります。滅多に使わないコマンドだと、数年たって、誰かが何気なく押してみてはじめてエラーがわかる、ということもよくあります(~_~;)エラーがなかなかつぶせないのはこの、サブルーチン化の段階で生じているということがよくあります。

しかも、必要な情報が不足しているエラーが出ればまだしも、出ないで、0値が放り込まれたと解釈して普通に動くこともありえます。普通に動くとはいえ、意図した通りには動かないので、結果としてはエラーなんですけど、流れとしてはつながっているので動いてしまいます。表示など、ユーザが気づく部分ならまだしも、ルーチンの内部の話だと、いったいどこにエラーがあるのかわからず、非常にバグとりに苦労することになります(*_*)

話がややそれましたけど(^^;;結局、便利なサブルーチンは便利であるがゆえ、いろんな場面で使われることが多くなり、いろんな場面で使われれば使われるほど、仕様の変更が非常にシビアになります。とはいえ変更しなきゃ使えないなら、変更してみないと仕方がありません。変更してみて、どのルーチンがそのサブルーチンを使っているのかを全部洗い出して、必要なら変更したり、テストしてみないといけません。それでもまた別のルーチンで呼び出すことになり、また仕様が変更になって……(ToT)

便利なサブルーチンには便利なゆえの悩みもありますが、さほど使っていないサブルーチンにも、無名故の悩みもあります(^^;というのは、えっと、なんて名前で、どこにあったっけ…?という…(^^;;

手続指向型プログラムでは、機能別サブルーチンが付き物です。機能別とは、先程からお話している、武力データ取り出し機能とか、武将データベース検索機能だとかです。ルーチンからサブルーチンとして切り離し、別にまとめるんですけど、まとめ方がうまくないと、迷子になります。しかも、当然名前も忘れてることが多いでしょうから、プログラムのどこかから探し出さねばなりません。全体が長くなればなるほど、サブルーチンが短ければ短いほど、だんだん見つからなくなってきます。

名付け方も問題で、わかるように書けば書くほど長くなり、便利な呼び出しサブルーチンだと、その長い名前が邪魔くさくなり、短くしたくなります。しかし当然、勝手に短くすれば、他の部分もなおさなければならなくなり…(T-T)そのうえ機能別なので、安易な名前だと他とかぶったりしてまぎらわしくなります。まぎらわしいだけでもやっかいなのに、間違って呼び出したりしたら…!

こうやって手続指向型プログラマは、覚えておかないといけないことが増えていくのです。全部の流れを覚えていたうえで、個別のサブルーチンの名前だとか仕様だとかも知っておかないといけません。忘れてもいいんですけど、必要なとき探すのが大変になるのはよくあることで(^^;;逆によーく見たら、同じ機能のサブルーチンをまた作った、とか…

手続指向型プログラマとしての歴史が長いのでどうしても手続指向型プログラマのお悩み開陳風になりますね(~_~;)それがオブジェクト指向プログラムなら解消されるのか、というと、なんと解消されるのです!(^O^)

まず、オブジェクト指向では、機能別ではなく、仕事を主体するのはオブジェクトです。武力データを持っているのは武将オブジェクトです。武将の武力データを知りたいなら、武将オブジェクトに問い合わせればよいのです(というより、オブジェクト指向ではそういう発想をしなきゃなりません(^^;;)。まず、武将選択リストオブジェクトから武将を選択し、そいつに何をさせたいかを選びます。それがボタンであるか、右クリックメニューであるかはお好みですけども(^^;;とにかくUIオブジェクト衆が、ユーザ様は、関羽の武力データを知りたいという命令をお出しになった、という情報を得ます。それを武将オブジェクトに投げます。

そこからは武将オブジェクト側の問題。武将オブジェクトに、武力データ回答機能を付けます。といっても、単にどの武将か教えてもらったら、その武将オブジェクトの武力データを返す、というだけなんですけどね(^^;;とはいえ本来武将オブジェクトは関羽や張飛といった個々の武将オブジェクトを意味するので、誰が、という情報は、個々の武将オブジェクトには関係ない情報なんですけども、今回全員ひっくるめての武将オブジェクトという発想をとりました。これは、リストオブジェクトと武将オブジェクトをくっつけて、武将オブジェクト側の操作ということにして、問い合わせるUIオブジェクト衆にとっては武将リストオブジェクトやらその他のオブジェクトは気にしないという作りにしています。というのも、ユーザ様が誰をお選びになるかまったくわからんが武将に対する操作、ということは多いんですけど、関羽に対する操作、ということはそれ程多くないからです。武将オブジェクトは、個々のオブジェクトを意味する以上に武将全体、という発想をとりました。

そうすると、どこの流れであっても、武力データを知りたいときには、武将オブジェクトに問い合わせるのが自然だな、と(私は)思うので、まず武将に問い合わせようとするわけです。そうするとDelphiでは親切にも、「busyo」(←武将オブジェクトの名前)と入力して、「.」(ピリオド)を打つと、「buryoku(busyoNo)」とか、「tiryoku(busyoNo)」とか、「namaehenko(busyoNo,henkogononamae)」だとか、自分で作った機能も選択してリスト形式で選ぶことができるのです。こうすると、武将に関することは武将オブジェクトに聞けば、機能一覧表が出るので、いちいち覚えておかなくともよくなります。これが最初の、オブジェクト指向だと頭が楽になる、ということなのです(^O^)

さらに、こういう機能を使うための必要な情報やオブジェクトが返してくれる仕事の結果だとかは、機能を作った際にきちんと設計しておく必要があります。特にDelphiはこういう設計に厳密なので、間違っていると動く前にエラーが出ます。そのため、オブジェクト側の機能の仕様変更があった時、以前の仕様で作ったルーチン(というのは変ですけど(^^;;)で呼び出している部分は、仕様が違うだけでエラーが出ます。たとえば、武力回答機能にはbusyoNoだけでなく、勢力情報も必要だということになった場合、従来のbusyoNoだけで呼び出している場所で、SeiryokuNo情報が足りません、というエラーが出ます。これで、先程の手続指向型プログラマを悩ますエラー原因が、動かす前に判別します。

ただ、SeiryokuNoなんて勢力オブジェクトにかかわる情報は、武将オブジェクトにとっては範疇外という設計にした方がいい気はしますね。ある勢力の武将オブジェクトの武力データがほしいならば、呼出元の方で、勢力オブジェクトと武将オブジェクトを組み合わせるべきでしょう。武将オブジェクトと勢力オブジェクトが別に存在するなら、それぞれ別にする方が(私には)自然です。もちろん勢力オブジェクトがなくて、武将オブジェクト自体に勢力情報を埋め込んでいるなら別ですが、あんまり勢力情報が必要なら別分けするべき、というのがオブジェクト指向的によりよいんではないかな、という気がします。

と、すっかりいっぱしのオブジェクト指向プログラマを気取っていますが、まだオブジェクト指向プログラムを作りはじめて1・2週間だったりします(^^;;しかし、従来の手続指向型プログラムで非常に苦労した点が解消されて頭が軽くなっている!というのは、プログラムをしていて非常に楽しい経験ではありますね(^O^)

index

〔TopPage〕

このページへのリンクはフリーです。
このページについてのご意見、ご質問などは、kr_ryo_green@yahoo.co.jpまでお願いします。
Copyright 2005© kr_ryo All rights reserved.
訪問件数