fc2ブログ

メソッドの再定義による競合問題・改

今回は昔書いた記事の改定版です。
昔のは、他のお知らせと一緒だったりMV版だったりと問題とか不足とかあったので書き直します。

ざっくり説明すると、メソッドの再定義のやり方によっては競合が起こるという問題です。
自分が作ったプラグイン同士でも起こりえます。
心当たりのあるプラグイン製作者さんは、プラグインを見直してみてね。

起こりうる競合の内容

下記のような二つのプラグインがあったとします。

プラグイン A
// this._a は初期化済み
const _Window_Base_update = Window_Base.prototype.update;
Window_Base.prototype.update = function() {
    _Window_Base_update.call(this);
    this._a++;
};

プラグイン B
// this._b は初期化済み
const _Window_Help_update = Window_Help.prototype.update;
Window_Help.prototype.update = function() {
    _Window_Help_update.call(this);
    this._b++;
};

どちらも内容はほぼ同じ。
違いは Window_BaseWindow_Help かというだけです。

ですが、プラグインAは問題ないのに対し、プラグインBは競合が起こる可能性があります。
どういう問題が発生するかわかるでしょうか?
ヒントは、 Window_Base クラスには update メソッドがありますが、
Window_Help クラスには update メソッドがないということです。


では実際に動作させた場合、どうなるか見てみましょう。

実行結果

▼ プラグインAを導入した場合
全てのウィンドウで update が呼び出されるたびに this._a が+1されます。
問題ありません。

▼ プラグインBを導入した場合
ヘルプウィンドウの update が呼び出されるたびに this._b が+1されます。
問題ありません。

▼ プラグインAとプラグインBを導入した場合
全てのウィンドウで update が呼び出されるたびに this._a が+1されます。
ヘルプウィンドウのみ this._a だけでなく this._b も+1されます。
問題ありません。


では何が問題なのか?

実はプラグインBの後にプラグインAを導入した場合、
ヘルプウィンドウの update が呼び出された際、 this._a は+1されず this._b だけが+1されます。
導入順を逆にすると競合が起こるのです。

競合の原因

違いは先にも書いた通り、Window_Base クラスには update メソッドがあり、Window_Help クラスには update メソッドがないことです。
なぜそれだけで競合が起こるのか?

その原因はプラグインBの2行目にあります。
const _Window_Help_update = Window_Help.prototype.update;
2行目では再定義前の Window_Help.prototype.update を変数に入れてます。
再定義ではよくある手法です。

ですが、 Window_Helpupdate メソッドはないので何が入れられるかというと、
上位クラスである Window_Baseupdate が変数に入れられます。

この updateプラグインAによって再定義される前の update です。
(この時点ではまだプラグインAが読み込まれていません。)

そうなると、その後のコレ↓
_Window_Help_update.call(this);
では、プラグインAで再定義される前のものが常に呼び出されることになります。

なので this._a が+1されないわけです。

導入順によっては起こらない競合なので、
知らないうちに自分が作っているプラグイン同士で競合が起こる
なんてこともありえます。


ちなみにこれは、ツクールのようなクラス定義で起こる問題です。
function Window_Base() {
    this.initialize(...arguments);
}

Window_Base.prototype = Object.create(Window.prototype);
Window_Base.prototype.constructor = Window_Base;

Window_Base.prototype.initialize = function(rect) {
    ...
こんな感じの。

class 構文によって定義されたクラスの場合、 super が使えるのでこういった問題が起こりません。
なので、プラグインで専用のクラスを作る場合は class 構文使うのがオススメです。

スマートな解決法

私はこちらの記事で紹介されているコードを使わせてもらっています。
const __base = (obj, prop) => {
    if (obj.hasOwnProperty(prop)) {
        return obj[prop];
    } else {
        const proto = Object.getPrototypeOf(obj);
        return function () { return proto[prop].apply(this, arguments); };
    }
};

const Game_Actor_addState = __base(Game_Actor.prototype, "addState");
Game_Actor.prototype.addState = function (stateId) {
    Game_Actor_addState.apply(this, arguments);

    // ...
};

これなら、ちょっと修正するだけで対応できます。

スポンサーサイト



tag : RPGツクールMZJavaScript

2022-07-17 : スクリプト日記 : コメント : 0 : トラックバック : 0 :
コメントの投稿
非公開コメント

« next  ホーム  prev »

プロフィール

木星ペンギン

Author:木星ペンギン
ほぼツクールのことばかり書いてます。
名前は↑から取りました。
木製ですが木星です。
トカゲは関係ありません。

ゲーム

  • 箱庭の勇者たち(体験版)
  • ぼくらの大革命!
  • 走ってぶつかるゲーム
  • ビート☆サムライ

メールフォーム

wood_penguin@yahoo.co.jp

名前:
メール:
件名:
本文:

月別アーカイブ

広告

寄付(Donate)