JavaFXでSingleton

少しだけ話題になっている(?)風なのでJavaFXでのSingletonの実現方法について考察してみます。
まず、JavaFXの特徴としてstatic変数(メソッド)がありません。これに対応するものとしてはスクリプトレベルの変数(関数)が、だいたい似たような機能を果たします。
したがって、第1の方法として次のようにインスタンスを1つスクリプト変数に定義しておく事が考えられます。

public-read var INSTANCE = SingletonClass {};
public function getInstance():SingletonClass {
    return INSTANCE;
}
class SingletonClass {
}

id:skrbさんもこちらのエントリーで書いてますし、casperさんのこちらのエントリーでも言及されている方法です。

以下、ブログcasperより引用。

Java で Singleton するには,「コンストラクタのアクセス修飾子を private にしてインスタンスの生成を制限する」必要がある.さて,コンストラクタの無い JavaFX Script ではこのテクニカルな技が使えない.どうしようか

class InternalSingletonClass extends SingletonClass{ }

def INSTANCE = InternalSingletonClass{};
public function getInstance():SingletonClass{
return INSTANCE;
}
public abstract class SingletonClass {
public var name;
public var number;
}

Singleton したい一家の大黒柱的クラス(SingletonClass)をいっそのこと abstract にして生成を制限する.大黒柱を継承させた default なクラス(InternalSingletonClass)を静的関数 getInstance で返すようにすれば,これであなたもたちまち Singleton に.

http://133.20.241.45/casper/entry/178

確かにsingletonの要件の1つとしてはインスタンスが1つしか存在しないことを保証する的な事があります。しかし、最近はDIコンテナにインスタンスのライフサイクルの管理を任せる事も多く、Singletonとして使うがコンストラクタはブロックしない事も多いです。個人的にはここまでしてインスタンスの生成を防がなくても・・・と思います。

また、こんな方法も提案されています。

class InternalSingletonClass extends SingletonClass{}

public-read def INSTANCE:SingletonClass = InternalSingletonClass{};
public function getInstance():SingletonClass{
return INSTANCE;
}

public var name;
public var number;
public abstract class SingletonClass {}

フィールドをすべて static にしてしまい,インスタンスからそのフィールドを参照するという荒技.継承しても値は変わらないし,static な値は override もできないので一応やりたいことはできそう.

http://133.20.241.45/casper/entry/178

要はstatic変数にしてしまえ…ということなんですが、それならばインスタンスとして管理しなければいいような気がします。

個人的にはインスタンスの生成のブロックまでは不要と思いますが、どうしてもユーザが信用ならないというならば次のようにすることでインスタンスの生成で例外となります。

public-read var INSTANCE = SingletonClass {};
public function getInstance():SingletonClass {
    return INSTANCE;
}
class SingletonClass {
    init {
        if (isInitialized(INSTANCE)) throw new IllegalStateException();
    }
}

isInitializedはビルドイン関数の1つで初期化されているかを返す便利な関数です。initにこれを仕込んでおけばINSTANCEの初期化時は通りますが、以降のインスタンス生成時にはIllegalStateExceptionが発生します。

実用レベルで考える

さて、実用レベルで考えた場合なのですが、JavaFXではSingletonなクラスは使用すべきではないと思います。何故ならば、インスタンスの生成が宣言的で解りやすいため、あるクラスのインスタンスに関しては関数(ファクトリメソッド)を呼ぶ・・・という事になるからです。これは混乱の元かな、と思います。
しかし、特定の状態はSingletonに保ちたい場合はあります。そのような場合は、内部状態をSingletonクラスに保持するほうが良いかと思います。つまり、こんな感じです。
SomeClass.fx

/**
 * SomeClass.fx
 */
def SINGLETON_VALUE:SingletonClass = SingletonClass {};
class SingletonClass {
    var value:Integer; // すべてのインスタンスで共通の値
}
public class SomeClass {
    public var value:Integer = bind SINGLETON_VALUE.value with inverse;
}

こうするとインスタンスは大量に作られても共通の状態や値を持てますね。