Home: Up: Back

Singleton

インスタンスの生成方法を制限し、生成可能なインスタンスの個数をコントロールする。
共有オブジェクトの提供を可能にする。


通常、オブジェクトが複数のクラスから共有される構造を構築するためには、以下のようなコードが必要になります。

class Share {・・・}
class A {
  private Share shareObject_;
  public void setShare(Share share) {
    shareObject_ = share;
  }
}
class B {
  private Share shareObject_;
  public void setShare(Share share) {
    shareObject_ = share;
  }
}
class Application {
      :
    A a = new A();
    B b = new B();
    Share shareObject = new Share();
    a.setShare(shareObject);
    b.setShare(shareObject);
      :
}
// この限りではない。A/B内でShareを生成したりする方法もあります。

しかし、これではApplicationまたはA/BのようなクラスでShareオブジェクトの生成/受け渡しを行うことになるので、結合度が強まってしまったり、メンテナンス性を下げることになります。

そこで、Share自身が、共有されるべき同一のオブジェクトを返すインターフェイスを持つようにすることで、A/B/Applicationの間でShareオブジェクトのやりとりを行うことなく、オブジェクトの共有を実現できます。

この段階(この目的を達成することだけを見ると)ではSingletonパターンとはいえませんが、Singletonパターンで得られる効果の一つと考えます。
class Share {
  private static Share shareObject_;
  public static Share getShare() {
    if (shareObject_ == null) {
      shareObject_ = new Share();
    }
    return shareObject_;
  }
}
class A {
private Share shareObject_ = Share.getShare(); : }
// この方法の場合A/Bにメンバを持たずとも、使うときに // Share#getShare()を呼んでもよい。

この場合、クライアントがnew Share()としてしまうと、共有ではないオブジェクトが作れてしまうので、これを禁止して、常に共有オブジェクトとしてしか利用できないようにします。

private Share() {}  // コンストラクタの非公開化

これで、Shareのオブジェクトは複数生成することが不可能になります。
これがSingletonパターンの基本ですが、このようなクラスは、実は単に全てのフィールド/オペレーションをstaticにしたものを使っても同じです。

Singletonパターンが全てをstaticにしたクラスに対して勝れているのは、クラスではなくオブジェクトとして唯一であるため、オブジェクト個数を一つ以外に制限することができる点と、うまくすればサブクラスを返させるようにしてポリモーフィズムによる拡張が可能という点です。

しかしprivateコンストラクタを用いてしまうと、サブクラスのインスタンス化が不可能になってしまうため、この効果が得られません。
また、C++ではprotectedコンストラクタにすることでサブクラスのインスタンス化を可能にしていますが、Javaのprotectedはパッケージ内の他のクラスからもアクセス可能なため、この目的に沿いません。
(パッケージ管理をちゃんと行えていればそれでもよいのですが)

従って、Javaでは素直にSingletonを実装することはできないということになります。

Singletonの継承を捨てるなら話は別ですが、それならば、全てをstaticにすればよい話です。
まあ、全てstaticは見苦しいからあまりしないとは思いますが。

Javaで完全なSingletonパターンを実現するためには、interfaceと内部クラスを用いたトリッキーな方法を使わざるをえません。

public interface SingletonInterface {
  public void method();
    :
}

public class Singleton implements SingletonInterface {
  private static SingletonInterface singleton_;
  private Singleton() {
    :
  }
  public static SingletonInterface getSingleton() {
    if (singleton_ == null) {
      // Singletonのサブクラスのどれを使うかを決定する部分
      // 決定方法はいろいろある。
      if ("sub1".equals(System.getProperty("singletonclass")) {
        singleton_ = new SingletonSub(new Singleton());
      } else {
        singleton_ = new Singleton();
    }
    return singleton_;
  }
  :
  public void method() {
    :
  }

  // コンポジションでサブクラス化と同等の効果を得る。
  // インスタンス化はOuterClassからしかできない。
  private class SingletonSub implements SingletonInterface {
    private SingletonInterface superClass_;
    SingletonSub(SingletonInterface superClass) {
      superClass_ = superClass;
    }
    public void method() {
        :
      superClass_.method();
        :
      return;
    }
      :
  }
}

まあ、ここまでして完全なSingletonパターンを適用したいケースもまれだと思いますが・・・。
普通は、単に共有オブジェクトにしたいだけなので、先に示した実装にしてしまうところです。
もちろんこの場合、全てをstaticにしたクラスでも同じということになりますが。差異があったら教えてください。

或いはやはり、コンストラクタをprotectedにして、同パッケージ内の他のクラスからはnewしないように注意するというのが現実的な解なんでしょうね。