こける Wired-オブジェクト指向異聞-状態オブジェクトの話

更新日付 '97/05/19

さてと、状態オブジェクトの話をしましょうか。ちょっと長いですが、しばらくおつき合いくださいませ。
念を押しておきますが、これからお話しすることは、普通の「オブジェクト指向」ではありません。異端ですよ。
「そうか、オブジェクト指向ってこういうのかぁ」なんて思わないでくださいね。(^^;;

プログラムを組んでいるお人の中には「状態遷移」っていう技法?を使っている方も多いんではないかと思います。
最近はオブジェクト指向設計でも「オブジェクトの状態遷移」ってのが出てきますしね。
普通のアプリケーションを組んでいるみなさんには、割と最近の話かも知れませんが、組み込みプログラマの私たちには「状態遷移」ってのは昔っからの常套テクニックなんです。
ってのは、「イベントに反応して動く」というイベント駆動型のプログラムは「割り込みで動作する」組み込みプログラムでは必須なんですな。
スイッチ押したり、センサーが反応したりってのが割り込みで入ってきますから。
で、イベント駆動型のプログラムに「流れを作る」一般的な方法ってのが「状態遷移」なわけです。

オブジェクトってのが状態遷移をする場合、どの状態にあっても受け取らなきゃならないイベント、即ち呼び出される可能性があるメソッドってのは一緒です。
状態毎にオブジェクトの型、即ちクラスが変わる訳じゃありませんもの。
当然、「この状態ではこのイベントは無効」ってのはありますよ。でもね、それは「そのイベントが到着しても、その状態では無視する」ということで、絶対拒否ってわけにはいかないんです。
で、状態毎にそのオブジェクトの動作って違うわけです。同じ事しかしないんじゃぁ、状態に分ける意味がありませんものね。
「同じイベントを受け取る=同じメソッドが存在する」と「しかし、それぞれ動作は違う」のが一つのオブジェクトに存在するそれぞれの「状態」なんです。
「ん?どっかで聞いたフレーズだな」と思った方いませんか。私はそう思いました。
そう、オブジェクト指向の基本3つの内の一つ、「多態」そっくり。
多態ってのが、「同じメソッドを実行する事が出来て、でもそれぞれ動作が違うもの」ですからね。
一つのオブジェクトに存在するそれぞれの「状態」...これって、多態すりゃいいんでは?

でも外から見えるオブジェクトは「単体」でなければ、そのオブジェクトを使っている人が困ります。
でなければ、そのオブジェクトを使っている人、即ちメッセージを発行するプログラムが、そのオブジェクトがどの状態にあるかを知らなきゃならないですもの。
ポインタと多態を使えば、「基本型のポインタ」ですみます。でもそのポインタをそのオブジェクト使う他の人が付け替えなきゃなりません。
これは、「カプセル化」というよりも、「自律性」に反しますよね。
さて...

COMもどきってのを聞いたことがあります。(Nifty-ServのFProgのOOの部屋で)
要するに、「その都度ポインタを引き出せるオブジェクトと、実際に動作するオブジェクトに分けちゃえ」というわけです。
これは結構な案だと思います。入れ子クラスを用いてやると隠蔽も完璧。(^^)v
でも、私が考えたのはちょっと違います。だって、使う側が面倒なんだもん。(^^;;
あるクラスのオブジェクトが状態遷移するのかどうかで、使う側が使い方を変えなきゃいけない。
大きめのクラスのオブジェクトなら、「状態遷移は当然」ですが、さてバッファクラスのオブジェクトは状態遷移をするでしょうか?
これは実装次第ってやつですね。ただのバッファなら状態遷移はしないでしょうが、さてこのバッファが「X-OFF/X-ON」を制御するとなったら?
そんなことまで使う側は考えてられません。

私が考えたのは、「代表者を一人見せといて、実際の仕事は複数で受け持つ」って奴です。
「代表者」が、よその人からの注文や問い合わせを受け付けます。(=代表者にメッセージを送ります=代表者のメソッドを使う側は呼び出します。)
「代表者」は現在の「状態オブジェクト」に仕事を委譲します。
「状態オブジェクト」は、メソッド実行の結果自分の状態で無くなるなら、次の「状態オブジェクト」を指定して「代表者」にその旨連絡します。
その「状態オブジェクト」は「代表者」によって舞台を追われます。そしてご指名のあった「状態オブジェクト」が次に舞台に黒子として上がるわけです。
各状態をとおして記憶しなけりゃならない「属性」は、「代表者」が持っています。
それぞれの「状態オブジェクト」は作成されるときに「代表者」を教えてもらい、それを覚えています。
ま、代表者に「クラスチェンジ」(=仮想メソッドテーブルへのポインタ差し替え機能)があれば大変便利なんですが、C++じゃできません。
というより、型に厳しい言語じゃ無理ですね。
C言語やアセンブラで、「クラスもどき」を作るときには、この「クラスチェンジ」機能を盛り込んでおいてもいいんですが。

それぞれの「状態オブジェクト用クラス」は独自に「継承ツリー」を構成します。
「同じような状態なんだけどちょっと違う状態」ってのは結構ありますからね。
「移動中状態」の一種である「横に移動中状態」って、ほれisA関係。(^^;;
当然、継承関係上「縦に移動中状態」ってのは「横に移動中状態」とは兄弟になります。
#「属性ですむだろが」って?、まぁここは判りやすく説明するための例だから、と思って。(^^;;

ちなみに、この方法を用いると、一つの開発で作成されるクラスは「小さいクラスがたくさん」というパターンになります。
で、この「小さい状態クラス」は、継承による再利用がしにくいです。なぜなら、自分の次の状態を知っていて、それしか扱えないから。
また、状態遷移をするような「大きなクラス」も継承による再利用がしにくいです。
これが最初に作り出す「小さなオブジェクト」から連鎖反応で状態が遷移し、差し替えるべきポイントを持たないから。
こういうのは工夫で何とかなりますが、工夫自体が重くなって再利用性が落ちるってのも困りますから、今んとこ私のアイディアはこれを突破していません。

この方法の簡単な使用例をば、紹介しましょう。
MS-DOS時代に戻りますが、PC98用とDOS/V用のマウスオブジェクトです。
「代表者」作成時に機種を調べて、PC98MouseオブジェクトかDOSVMouseオブジェクトか、どっちかを作成し属性として覚えます。
この属性の型はは、それぞれの共通の親であるBasedMaouseクラスへのポインタか参照。
あとは、「代表者」は仕事のすべてをそれに委譲します。
外から見れば、「静的な単体オブジェクト」。作るのは「それぞれに特化したクラス」。
ちょっと簡単すぎたかな。遷移もしないし。

さて、静的な「状態オブジェクト用クラス」の集合が、動的にオブジェクトを作成されて「状態遷移」するというお話ししました。
欠点をもう一度確認すると、「継承による再利用がしにくい」です。
これは、ソフトウェア開発、それも「工場」という現場で私が思いつき用いている方法です。
アカデミックなお人たちからは「そんなものはオブジェクト指向とは認められん」といわれるかもしれません。
でも、泥臭い「工場」という現場からの声があなたに聞こえたらと思って、お話ししてみました。

by Kok.Wish


次のお話
[表紙] [Program Files] [オブジェクト指向異聞] [プログラム未整理知識] [Winsockを使ってみようぜ] [だべり] [What's New] [書いた奴] [リンク] 秘密