2005/7/22

オブジェクト指向なスクリプトのサンプル  VRMスクリプト禅問答
【怎麼生】
オブジェクト指向的にVRM4スクリプトを書く、というのがピンときません。何か具体例を挙げて説明してください。

【説破】 4.0.1.2
前回、オブジェクト指向とは「オブジェクトが自分がどうすべきかを知っている」ようにする考え方であることを説明しました。そこから進めて考えると、VRM4スクリプトを書くに際しては、あるオブジェクト(ポイント、センサー、編成etc...)にはそのオブジェクトがどう振舞うべきか、だけを書くのが理想です。そして、VRM4スクリプトは(完璧ではないにせよ)そうしやすいように設計されています。
たとえば、ポイントPOINT1があるとして、編成TRAIN1だけが反位方向に、他の編成は正位方向に走行するようなオブジェクトを書きたい、というケースを想定し、その考え方を見ていきましょう。まずは以下のサンプルコードをご覧ください。
[センサー(SENSOR1)スクリプト]

//操作対象ポイント設定
VarPoint CtrlPoint
get CtrlPoint "POINT1"
//反位通過編成の識別番号
Var ChkNo
set ChkNo 1
//判定用一時変数宣言
Var TmpNo
//センサーイベント設定
Var EventID
SetEventSensor MtdPChange EventID
//
//センサー検知時に動作するメソッド
//
BeginFunc MtdPChange
//センサーを通過した編成を取得
VarTrain ObjTrain
GetSenseTrain ObjTrain
//編成が目的のものと合致するか検証
mov this TmpNo ObjTrain StdVarTypeNo
ifeq TmpNo ChkNo
//編成のTypeNoとChkNoが一致すれば反位へ
call CtrlPoint StdMtdTurnout
else
//一致しなければ正位へ
call CtrlPoint StdMtdStraight
endif
EndFunc

[ポイント(POINT1)スクリプト]

//
//ポイント反位設定メソッド
//
BeginFunc StdMtdStraight
Var Tmp
set Tmp 0
SetPointBranch Tmp
EndFunc
//
//ポイント反位設定メソッド
//
BeginFunc StdMtdTurnout
Var Tmp
set Tmp 1
SetPointBranch Tmp
EndFunc

[編成(TRAIN1)スクリプト]

//走行線識別用の変数を宣言
Var StdVarTypeNo
set StdVarTypeNo 1
//以下はご自由に・・・
上掲のサンプルコードは「センサーSENSOR1を編成TRAIN1が通過したときに、ポイントPOINT1を反位に切り替える」という動作をします。VRM4では、走行する編成に連動して何かを自動化したい場合、センサーを設置してセンサーが編成を検知したときに「何か」をする、という風に考えます。ここに書いたスクリプトがまさにその「何か」です。どのへんがオブジェクト指向的なのか、見ていくことにしましょう。

第一に、「直値の回避」が挙げられます。直値、というのは、具体的な何かを特定する表現(つまり、オブジェクトの固有名)をあちこちに書いてしまうことを言い、オブジェクト指向プログラミングの禁忌の1つとされています。
たとえば call 命令(指定オブジェクトのメソッドを実行する)に "POINT1" と「直値」を書いてしまうと、 このスクリプトは対POINT1専用になってしまいます。これを避けるためサンプルコードではセンサースクリプトの冒頭でオブジェクト変数 CtrlPoint を宣言し、この変数にこのセンサーの制御対象となる POINT1 を get しています。以降、POINT1 を制御する際は、直値を使わず、変数を使っています。
この書き方の最大のメリットは、もう1つ、2つと同様のセンサーを増やしていく際に、メソッドについては一切書き換える必要がない点です。
これを「再利用可能なコード」と呼ぶこともありますが、オブジェクト指向の利点の1つは、まさしくこの点です。逆に非オブジェクト指向的なコードは再利用に際してあちこちにちらばった直値を変更しなければならず、手間がかかる上に問題が発生した場合の原因追求や修正も困難になります。

第二に、サンプルコードはセンサー上を編成 TRAIN1 が通過した際にのみポイントを反位に切り替えるのに、センサースクリプトの中には "TRAIN1" という文字が見当たりません。つまり、「通過した編成が TRAIN1 か否か」という判定は、厳密に言うとおこなっていないのです。にも関わらず、このポイントに他の編成を通過させてもポイントは正位になります。何故でしょう。
サンプルコードでは、ポイントを正反のいずれに切り替えるかの判断に「通過した編成がどのオブジェクトか」という基準ではなく、「通過した編成オブジェトのプロパティを参照する」という考え方をとっています。プロパティというのはVRM4スクリプトの体系では定義されていない言葉ですが、オブジェクト指向の世界では「それぞれのオブジェクトに共通して存在するが、具体的な値はそれぞれのオブジェクトで異なるもの」をプロパティと呼びます。VRM4の世界では、それぞれのオブジェクトのグローバル変数がこれに当たります。
具体的には、編成スクリプトのグローバル変数 StdVarTypeNo がそうです。センサーのスクリプト MtdPChange は、自身の上を通過した編成オブジェクト(GetSenseTrain 命令で判明する)からこの変数を取得し(mov 命令による)、この値が自身が反位方向に進めてやるべき番号(変数 ChkNo)と等しいかどうか(ifeq 命令)でポイントの切替を判断しています。
この方法には3つのメリットがあります。
まず、新たな編成を加える際、編成スクリプト側で StdVarTypeNo さえ正しく宣言すれば、センサー・ポイントスクリプト側には一切手を加えなくて良い、ということです。この例だけではピンとこないかも知れませんが、レイアウト内に同じようなセンサーとポイントの組み合わせが20コあることを想像すれば、そのありがたさがわかるはずです。
次に、編成スクリプト側で、たとえばあるキーを押すことで変数 StdVarTypeNo を変更するメソッドを用意しておけば、編成の走行経路を動的に変更するのも簡単だ、ということです。これもレイアウト内のセンサー・ポイントに直値が散らばっていると、とてもではないですが実現不可能です。
そして最後で最大のメリットは、編成のプロパティ StdVarTypeNo のルールさえ定めてしまえば、自分以外のVRMユーザーの作った編成を取り込んだり、レイアウトを拡張したりすることも、容易になる、という点です。すなわち、メソッドのみならず、編成やレイアウトそのものが「再利用可能なオブジェクト」になるのです。

前回、オブジェクト指向とは「オブジェクトが、それ自身がどう扱われるべきかを知っているようにすること」であると説明しましたが、このケースでは、編成オブジェクトが自分の通るべき経路を「知っている」点、そしてセンサー・ポイントオブジェクトがどの StdVarTypeNo の編成をどちらに通すべきかを「知っている」点、そしてこの両者がお互いに依存していない(StdVarTypeNoの約束事以外に何の制約もない)点が、まさに「オブジェクト指向的」であると言えます。
もちろん、ここに示したのは1つの例であり、必ずこうしなければならない、というものではありません。が、オブジェクト指向的な考え方を導入することにより、(1)動作の変更や問題の追跡が容易なコードが書ける、(2)約束事を介して他のユーザーとレイアウト資産の共有が容易なコードが書ける、という恩恵を被ることができるのです。

注)本連載で示すVRM4スクリプトや物の考え方をあなたが採用した結果生じる全ての事象については、その責を負い兼ねます。私の書いたものを読むに際しては、常に「コイツは狂人で、でも一部は役に立つことも言っているかも知れない」という程度の心構えで臨んでください。
0

2005/10/24  19:00

投稿者:ghost
補足:
>プロパティというのはVRM4スクリプトの体系では定義されていない言葉ですが
最新のVRM4スクリプトリファレンスではグローバル変数について「オブジェクトのプロパティーとして外部に公開されます」と言及されている。

コメントを書く

この記事にはコメントを投稿できません




teacup.ブログ “AutoPage”
AutoPage最新お知らせ