ああ、もう来週で激動?の2004年のこの徒然日誌も終わりを迎えようとしています(^-^)っていや、別に最終回ではありません(^^;;それにしてもこの形態、長いこと続きましたね〜(^o^)この日誌はかつてメルマガみたいなものって書いていましたが、これって最近大はやりのブログそのものですね(^^;新着順に並び、週1更新、日誌。ブログがはやり、長いこと続きやすいのもわかる気がします。正直書きやすいですからね(^-^)
さてさて。前回オブジェクト指向についての理解を深めるために、また、正しい理解かいなか批判にさらしてみようと説明をはじめました(^o^)時間切れで途中で途切れましたが、前回でオブジェクトとは、と、オブジェクト指向の3要素のうち、カプセル化と継承についてお話いたしました。そこで今回はその続き、最後の要素である多態性と、実際の使用としてのインスタンス化についてお話したみたいと思います(^-^)
オブジェクト指向はオブジェクトたる「何か」を、分析したり抽象化したりして求めます。抽象化方向がオブジェクト指向にとって重要だ、というお話もしましたね。抽象化を突き進み、必要な内容を全部含めきってから、どうしても分けるべきところとして、下位に継承していきます。
継承されたオブジェクトには、継承が必要な以上、並列に別の継承されたオブジェクトが通常あるはずです。そうでなければ継承なぞせず、上位のオブジェクトで十分必要を満たしてしまえるからです。たとえば兵器オブジェクトからは、それぞれ違いがあるからと継承された、戦艦オブジェクト、戦闘機オブジェクト、機動歩兵オブジェクトが登場するというような感じです。それぞれ「兵器」でひとまとまりにされながらも、それぞれ本質的に違う内容があって別々にされたはずです。こう考えていくと、実は機動歩兵と戦闘機の違いが非常にあやしく(^^;;区別が必要なのか、そもそも機動歩兵なんてものが必要なのかどうかわからなくなっていきますが(^^;;;そこはお約束、ということで…(^^;A
さて、司令官であるプレイヤーは、兵器オブジェクトに攻撃を命じます。兵器オブジェクトはそれぞれ、主砲だったりビームだったり機関砲だったりで攻撃します。プレイヤーはこの兵器はこの武器で攻撃せよ、と命じたわけではなく、単に攻撃せよ、と命じるだけで、それぞれの兵器オブジェクトはそれぞれの武器で攻撃します。これが多態性です。
これを戦闘機オブジェクトやら機動歩兵オブジェクトやらを兵器オブジェクトの継承によって作成せず、兵器オブジェクトの中のデータの違いで表現したとします。そうであっても武器データが異なれば、それぞれの武器で攻撃するのはもちろんです。そういう意味では継承によって差異を表現する必要はありませんね。この辺はオブジェクト分析によって検討すべきところです。多態性として重要なのは、兵器オブジェクトの持つ、攻撃という方法について、継承されたオブジェクトがそれぞれ別々の方法を取ろうがなんだろうが、「攻撃」という上位オブジェクトの持つ手段で命じさえすれば、下位オブジェクトはそれぞれの方法で攻撃するということなのです。
これだけだと、なんだい当たり前じゃないか、という感じもします(^^;ところがこれを手続指向型で表現するとどうなるか。まず、攻撃命令を対象に出します。攻撃命令を受け取った対象がどの兵器のどの機種なのかを判定します。その判定は、兵器機種ごとにずらーっとリストアップされていることでしょう。そのリストは全兵器/機種分そろっていないといけません。ここでひとつでも抜けていれば、その機種を使った攻撃ではエラーが出ます。ようやくどの機種か判別したところで、攻撃武器が判定されます。もし武器が2種類以上あれば大変です。さらにその具体的な兵器が武器を2種類持っているのかどうか判定し、2種類持っていたらどちらを使うのか判定し、ある1種類を選択したとして、その武器のデータを参照しにいきます。それがビームだとして、そのビームを使った命中判定を行い、防御対象の防御判定、破壊力判定…と、延々手続を羅列していきます。いやあ、ようやくこれである兵器慈無の攻撃処理ができたのです。さて次は機関砲を持つ座苦の部分を作ろうか、やれやれ…(~_~;)
こうしてみると、1つのコマンド処理だけで嫌になったのがおわかりいただけたでしょうか(^^;違いがあればその違いすべてを一度に表現しなければならないのです。たった1つのコマンド処理とはいえ、10の施設建設なら、10の微妙に違った処理の流れを、途中の枝分かれも含めて間違いなく作っていかなければならないのです。武器兵器の違いなんて、1つ武器を追加するだけで、関連する部分すべてについて、追加した武器分の判定や結果の枝分かれを全部正しく記述しなければならなくなります。そのため、だんだん細かい差異なんてしまいにどうでもよくなったりするんですよね〜(TдT)
まさにこのことが手続指向型の限界であり、私が途中で嫌ん(TдT)になってしまってオブジェクト指向を必死で勉強する羽目(^^;;になったのもわかるというものです。手続指向型では、違いがあるならその違いを常に全部一度に表現しなければならない、それは、1本のスタートから、次から次へと枝分かれする道、もしくは巨大な樹形図を思い浮かべていただければ一番イメージどおりだと思います。その枝分かれは、途中でくっつくこともありながら、たとえばビームと機関砲の違いですら枝分かれの原因になるのです。1本作って枝分かれの量がわかれば、それがコマンド分だけあると想像すれば、途中でからまるな、と思わない方がおかしい(ToT)しかも、それは全部表現しなければならないのです!違いの部分を表現しなかったり、間違ったりすると、それだけで不具合の原因になるのです!!(~_~;)
では、それがオブジェクト指向では解消されるというのか?されるんですよね〜(^^;そこが多態性のいいところです。一番上位の抽象化されたオブジェクトがあるとします。そいつは命令を受ければ行動します。個々具体の細かい差異は、上位オブジェクトを継承した下位オブジェクトで、その差異のみ表現します。そうすれば、同じ命令を与えているのに、上位オブジェクトと下位オブジェクト、また下位オブジェクト同士などでは違うことをします。
具体的に言うと、兵器オブジェクトに対する、攻撃させる、という部分さえ作っておけば、細かい差異は兵器オブジェクトから継承された戦闘機オブジェクトの機関砲だろうがビームだろうが、戦艦オブジェクトの主砲だろうがミサイルだろうが、攻撃させる命令にとっては違いはないのです。攻撃命令を受けた後の手続は、兵器オブジェクト自身が持っています。戦闘機オブジェクトはそれを継承しているので、自分自身では持つ必要もありません。命令を出す方も、出した先がたまたま戦闘機オブジェクトだったから、それが機関砲を撃ったという結果を得ることになるだけです。別にそれが戦艦オブジェクトだろうが機動歩兵オブジェクトだろうが、攻撃させる、という命令は同じで1つしか必要ありません。
もちろん兵器オブジェクトの中では、攻撃する際の武器の選択やら命中判定やらの手続は用意する必要があります。しかし、個々具体のどの武器かどうか、命中判定の違いはどうか、といった内容は、下位オブジェクトで持たせればいいのです。また、そういう具合に抽象化するべきなのです。上位オブジェクトではもやもやした流れだけ持ち、下位オブジェクトでははっきりした内容を当てはめていくという感じでしょうか。そのため、下位オブジェクトごとに違ったことをするように見えます。それが、多態性、なのです。
ところで、これを、攻撃オブジェクトや移動オブジェクトという風に機能別に考えて、その違いを継承によって差異化するとすると、手続指向型に化けてしまうようです。やはり「もの」をオブジェクトとして検討するべき、ということでしょうね。攻撃も移動も、兵器オブジェクトのふるまいのひとつとして、その攻撃の方法、移動の方法の個々具体的な違いを多態性で表現するというところでしょうか。話はそれますが、しかし、なぜでしょうか?
まずもって、攻撃や移動という内容は、行動や機能であって、手続ともいえます。さらに攻撃オブジェクトの中身には、行動対象の中身が含まれていません。結局行動対象や行動される対象や、行動対象のデータや行動される対象のデータなどを用意してこの攻撃オブジェクトに放り込んでやらないといけません。これでは手続指向型とそっくりです。なるほど、だから機能別差異化はだめなんですね、独立した「もの」を用意しなくちゃあならない(^-^)
こうやって、実際に使うべきオブジェクトを、いろいろ分析したりして設計していくんですね。兵器オブジェクトなら通常できることはすべてこれに含め、たとえば兵器の搭載なんて部分を、戦闘機オブジェクトなら持たなくても戦艦オブジェクトなら持つ、とか、いろいろ機能を分析すれば、そのままオブジェクトができてくるのです(^-^)
そうやってできた、オブジェクトの原型をクラスと呼びます。これは鋳型になぞらえることが多いですね。クラスはまさに、型、です。たいやきやたこ焼きを作るような、また、プレス加工の際の金型みたいなものです。たいやきも粉や砂糖やあんこの量によって、見た目は同じたいやきでも味は全然違ったりします。同じプレス加工でも、プレスされたのが鉄か銅かアルミかでこれまた全然違うものになります。
その、実際にプレスされたようなものを、インスタンスと呼びます。クラスからオブジェクトをインスタンスするというと呪文のようですが(^^;武将クラスから張飛や関羽をインスタンス化するといえばわかりやすいですね。ここでいう張飛や関羽は、武将クラスで設計されたオブジェクトで、武力が抜群にあっておつむはちと…(^^;というデータを有しているのです。インスタンスが、プログラムが実際に動かす際に登場する「もの」、つまりオブジェクトであって、たとえばボタンやリストもインスタンスです。こいつらはクラスを原形に、中身に入れるデータを別々に持った実体、実例です。無名武将として設計されたクラスから、関羽や張飛などのインスタンス化された武将が量産されるような感じでしょうか。
さてさて、最初どうもなじめなくて、また、もしかすると間違っているかもしれないと思うのが、たとえば武将オブジェクトとしての関羽や張飛など、登場する武将は全部オブジェクトとしてインスタンス化しないといけないのか、ということです。
どういうことかというと、まず、武将オブジェクトは必要な時だけインスタンス化して、不要になれば消せばいいという発想があるんですよね。そうしないと、プログラムを起動させたとたん、100人だか1000人だかの個々具体の武将オブジェクトがどかんと生産されます。使うこともあれば使わないものもあったとしても、全部生産されて、生きて動いていたり、死んでいたり、子供だったりするわけです。こういう連中は全部、最初のインスタンス化の際にデータを与えられます。後はプログラム上で「もの」といえば語弊があるけどもオブジェクトとして勝手に動いたりするわけです。イメージとしてはそうですね。
しかしそうこう考えていくと、せっかくこれまでデータベースで用意してきた内容が全然いらなくなってしまいます(*_*)こういうデータベースのデータは、最初のインスタンス化の際にだけ呼び出してオブジェクトに放り込めばよく、後はオブジェクトの中に存在して、オブジェクト自身がデータも操作したりするわけですから、データベースはもう必要なくなってしまうのです(ToT)後は保存の時位でしょうか?
いや、まあ、オブジェクト指向によって便利になるのはなるので、あえてデータベースを使わなきゃならない、というより、今までの手続指向型よりはるかに便利になりそうな感じはするのでそれはそれでいいとして(^^;;問題は、プログラム上でできるほとんどあらゆることのできるオブジェクトがどかんと1000人なら1000個も作っちゃって、メモリが足りるんかいな?というところなのです。
その点確かに、オブジェクト指向だとメモリの節約にならないという批判があります。もともと手続指向型は、少ないメモリと遅いCPUでいかに効率よく処理するか、という発想で進化してきましたから、オブジェクト指向だとまるっきり逆方向です。まあ、効率よく、という点ではある程度以上の規模だとオブジェクト指向プログラムの方が早かったりするかもしれません。けれども、メモリの点は、いかにも巨大化しそうです(~_~;)
そこで実験。Delphiで単純なクラスを1つ作り、ほんの100万個ほどインスタンス化してみました(^^;;;何もなければ7メガのプログラムが、どかんと62メガくらいまで大きくなりました。いやあやっぱり(~_~;)
これに対して、メモリ節約の観点から少し考えてみました。コマンドを使う際だけ、たとえば張飛オブジェクトをインスタンス化したりするとします。で、行動データ内容やその判定結果などはデータベースに書き込んでいったん消滅させるのです。武将のデータはデータベースに常にあって、インスタンス化するのはただひとり分、という感じです。そして、たとえば命令終了後の実行や行動にあたっては、ひとり一人順番にインスタンス化しては適切な処理をしては消滅させるのです。そうすればメモリが節約されます……
一見して、オブジェクト指向的でもあり、メモリも節約できていいようにも思えます。しかし、オブジェクト指向的とされるプログラムでは全部のオブジェクトをインスタンス化しているようです。そう考えると間違ったアプローチにも思えますね…(~_~;)
それから、このオブジェクト指向プログラムでの別の問題点も思いつきました(~_~;)たとえば武将クラスを作ったとして、細かい武将個々人特有の内容は継承で作ることができます。ところが、たとえば武将クラスの上位に、君主クラスや軍師クラスや在野クラス(まさに階級ですね(^^;)などを作ったとします。まあ、武将クラスを継承した下位のオブジェクトでもいいんですけども、これらの、たとえば在野クラスに、諸葛亮オブジェクトをインスタンス化したとして、彼を登用して、在野クラスから武将クラスへ、さらに軍師クラスへ引き上げたいと考えます…が、できないのです(~_~;)なぜかというと、諸葛亮オブジェクトは最初在野クラスとして生成されます。これを武将クラスへ引き上げる、というのは、概念としては可能ですが、いったん在野クラスから諸葛亮としてのデータを(データベースにでも)避難させた上で、その在野クラスでインスタンス化していた諸葛亮オブジェクトをいったん消滅させ、改めて軍師クラスで諸葛亮オブジェクトをインスタンス化させないといけません。これでは先程の、毎回オブジェクト化して消滅させるのとあまり変わりがありません。
もちろんこの解決法は、武将クラスを君主クラスや在野クラスなんてのに分離したり継承させたりしなければいい、ということなのですけども、なんとももどかしい話です(~_~;)オブジェクト指向では、抽象化と具体化によって様々なクラスを作り、それぞれを継承することによって色々なレベルや違いを表現することができます。たとえば、在野クラスであればほとんど何もできないのが、武将クラス、軍師クラスという風にレベルアップすることで、武将ができることを追加していく、ということが簡単に表現できます。
ただ、やっぱり、こういう形での身分の継承はやめた方がよい、という前回どおりの結論になるんでしょうね(~_~;)武将クラスは、武将という身分ではなく、登場人物ほどの意味で考えた方がよさそうです。そのため武将クラスは、とりあえず軍師のコマンドも一般武将のコマンドもなんでもできるようにしておきます。その上で、現在の身分によって、できることが変わったりするようにするのがよいのでしょうね。しかし、せっかくの継承ながら、こうなるとなんとももったいない感じです。
そうそう考えていくと、先程の、武将を呼び出すごとにインスタンス化し、命令終了とともに消滅させるという方法が活きてきます。というのも、毎回インスタンス化と消滅を繰り返すわけですから、身分を変えるためだけにオブジェクトを生成消滅するのはなんでもないことになるのです(^^;全武将を一度にインスタンス化していれば、たかだか身分を変えるだけで消滅再生成するのはすごい話ですが、武将にコマンドを言い渡すためだけに武将オブジェクトを消滅生成させているとするなら、たいしたことには見えないのです。
これを深く考えていけばいく程、だんだん毎回生成消滅の方が柔軟でよいように思えます…本当にそうでしょうか?オブジェクトは本当に最初から最後まで存在していた方がよいのかどうか、そこから検討してみましょう(^-^)
まず、全オブジェクトを最初から生成したとします。武将オブジェクト1000人分というやつです(^^ゞさて、これが有利なのは、プログラムが起動している間中、武将オブジェクトにはデータが保持され続け、いつでも動いてもらうことができるということです。一気に全オブジェクトに同じ命令を出すこともできます。
これは、動かす方、つまりユーザーと武将オブジェクトとの間をとりもつインターフェースオブジェクト、つまりコマンドボタン達も楽ができるわけです。武将オブジェクトが誰であっても、対象武将を指定して動けと命令を伝達すればいいわけですから。多態性、カプセル化の恩恵をしっかり受けていますね。
ところで、ここで命令を受けるのは1つの武将オブジェクトだけです。全武将オブジェクトに命令を与えるべきシーンというのはほとんどありません…ユーザーの命令終了後の全武将行動位でしょうか?それにしても、一度に行動できるのは1人のみでしょう。それならやはり1人ずつ順番にインスタンス化すればいいのでは…?
やはりこの方法で一番気になるのは、これがオブジェクト指向的でないのではないか、ということでしょう(~_~;)たとえば実はこのアプローチは手続指向的というような…ところが、全武将がオブジェクトとしてインスタンス化されていようといまいと、一度に使われるオブジェクトは1人分だけです。後は単にメモリ上に存在しているだけで、命令が来るまでは特に何をするわけでもありません。1人に対しての命令等の流れが手続指向型的だと言われるなら、何人分インスタンス化してようと同じことです。
もっとも、何かするたびにインスタンス化しないといけない問題点はあります。オブジェクト指向のモデルには、ファクトリークラスというような、ひたすらインスタンス化させる生産工場のようなクラスもあります。ここで武将1000人分インスタンス化するんでしょうか…そういった大量生産ファクトリークラスではなく小量注文対応ファクトリークラスも準備しておけば問題ないのではないでしょうか。
さらにさらに。いろんなことができる武将クラスですが、本来オブジェクト指向にしてみようと思ったのは、プログラムの流れが手続指向型だとわけがわからないほど分岐して複雑になるからでした。それだけの巨大な命令群を武将クラスひとりが受け持てば、単純にその武将クラスがあまりに巨大になりすぎて結局同じことになるのではないか、ということです。これは早々にオブジェクト分析を行い、何をもってオブジェクトと考えるのかを考えないといけませんね〜(*_*)
|index|
このページへのリンクはフリーです。