switch文を消すとは

switch文を消すとは

はいどーもこんにちは、人生のswitchを作りたい無職です。
複数人から「switch文を消したほうがいいってどういうこと?」と質問されたので(時期はバラバラですけど)、本日はswitch文を消すとはどういう意味なのかを自分なりに調べ、まとめてみました。
諸事情で言語はJavaScriptもといツクールMVですが、本当はC#で書きたかった記事。

switch文を多用したコード

例えばシーンごとに、startメソッドが実行されたタイミングで何らかの処理をおこないたいとします。
そのとき、以下のようなコードを実装する方法が考えられるでしょう。

上記のコードはScene_Baseで「現在のシーンは何か」をswitch文で判断し、実行する内容を振り分けています。
これくらいの長さのコードなら、switch文を使用しても全然問題ないでしょう。
ですがもしも全シーンで処理を振り分けたい場合、switch文が異様な長さになります。
また、各シーン固有の処理が2行ではなく30行程度の長さが必要な処理だとしたらどうでしょう。
startメソッドが異様な長さになることが想像できるかと思います(メソッドを抜き出しても事情は大して変わりません)。

サブクラスで処理を振り分ける

今回は「シーンごとに、startメソッドが実行されたタイミングで何らかの処理をおこないたい」のでした。
であるならば、Scene_Baseで全ての処理を振り分けるのではなく、サブクラスで処理を実装すればよいのではないでしょうか?
具体的には以下のようなコードになります。

switch文は消えました。
代わりにdoSomethingメソッドを呼ぶだけになっており、必要なサブクラスでdoSomethingメソッドをオーバーライドしています。
各シーンに固有の処理は各サブクラスにまとめられているので、各処理が長い場合はこちらの方がコードの管理も楽だと思います。

switch文を消すべきかどうか

上記の例ではswitch文を消すために、各サブクラスに処理を追加しました。
ですがswitch文は悪なのでしょうか?
これはケースバイケースだと思います。
上記の例そのままなら僕は間違いなくswitch文を使わないコードを書きますが、単純なswitch文を消すためだけに複雑度が増す場合、そのままswitch文を使う選択も大いに有り得るでしょう。
ですがもし上記のswitch文に相当する分岐が複数箇所に散らばっている場合、サブクラスに処理を閉じ込めることを検討する価値はあると思われます。

ツクールMVならではの方法

次の例に行きます。
二つ名を変更するとき、現在のステート状態を調べ、それに応じて二つ名に何らかのプレフィクスを付加したいとします。
例えば「不死身」状態で、設定したい二つ名が「英雄」なら「不死身の英雄」となる、とかですね。
「毒」状態で「設定したい二つ名が「無職」なら「修行中の無職」とかでもいいですね。
これをプログラムしてみたたのが以下のコードです。

これもswitch文がありますね。
今回はこれをなくしたいわけです。

さて仕様を確認してみると、ステート3番のときは「不死身の」というプレフィクスを、ステート4番のときは「修行中の」というプレフィクスを、ステート7番のときは「短期な」というプレフィクスをつけたいのでした。
言い換えると「ステートごとに得たい値が異なる」ということです。
こんなとき便利なのがメタタグです。

メタタグとはツクールMVに備わっている機能で、hoge.meta['メタタグ名']の名前で、各値を取得できる便利なしろものです。
今回はこれを使って解決してみましょう。

メタタグを使ってみる

まずデータベースを開き、各ステートに<addedName:不死身の>などをメモ欄に追加していきます(メタタグの詳しい使い方はヘルプを参照してください)。
その後、setNicknameメソッドを以下のように変更します。

switch文が消えたのがわかるかと思います。

振る舞いを変えるのは難しい

さて上記のmetaタグを使った方法ですが、いわゆるGoFのステートパターンと似ています(ステートパターンについてはこのブログでもちょいちょい触れている&ググったら大量に出てくるので説明は省略します)。
ですがステートパターンと違ってインスタンスごとに振る舞いを変えることは難しいです(データベースはJSONファイルとして出力されており、そこからオブジェクトを組み立てているため。また、データベース状に存在する各ステートは同じクラスに属すると考えてよい)。
上記のように単に各インスタンス(各ステート)で別個の値を設定したいだけなら問題ありませんが、各インスタンスで振る舞いを変えたい場合はどうしたらよいのでしょうか?
例えば以下のようなコードが考えられます。

先程のコードと変わったところは

の部分です。
doSomethingというメタタグが存在するなら、それをevalで実行するコードです。
データベースのメモ欄(不死身)には例えば以下のように書くことができるでしょう。

これでアクターが不死身状態のときにニックネームを変更した場合、コンソール画面に「音を鳴らすよ」と表示したあと、アイテムを使用した際の音が流れることになります。
もちろん他のコードを書くこともできますし、ステートごとに振る舞いを変えることも可能です(例えば特定のステート時に二つ名を変更するとアクターが死亡するとかw)。
これくらいならまだ理解できないことはないですが、メモ欄に以下のように書かれていた場合はどうでしょう?

このthisが何を指すのかはメモ欄を見ただけではわかりません。
setNicknameの処理内容を読んではじめて「あ、このthisはアクターを指すのだな」とわかります(そもそもJavaScriptのthisはややこしいので混乱のもとな気も)。
バイトコードを使ったデータ駆動開発に似ていて魅力的ではあるのですが、これってぶっちゃけややこしいですよね。
少なくともプラグイン利用者には優しくないと思います(自分一人で使うならまあ……って感じですが)。
とは言え最初の例のようにステートのIDで処理を振り分けるのもアレな気がするので、何かは諦めないといけないのかもしれませんね……(もっといい方法があるかもですが)。

終わりに

余分なものにも多分に触れましたが、いかがでしたでしょーか。
ツクールMVのデータベースのメモ欄を使って動的に処理を振り分けるのは、ちょっと使いにくいかもしれないなあ……と思いましたが、局所的に使うなら全然ありだと思っています(特に個人でしか使わないプラグインで、完全に動きを把握してるやつ)。
僕もまだまだ勉強中の身なので、これからも色々と研究していきたいと思いますよ。
ほなそんな感じでまた。

フォローする