モノトーンパターン


Singleton Patternとは?
シングルトーンモードは、アプリケーションの起動時に最初のメモリ(静的)のみを割り当て、そのメモリにインスタンスを作成して使用する設計モードです.
コンストラクション関数が複数回呼び出されても、実際に生成されたオブジェクトは1つしかなく、最初に作成された後に呼び出されたコンストラクション関数は最初に作成されたオブジェクトを返します.
すなわち、モノトーンモードは、インスタンスを作成するために使用される設計モードである.
インスタンスが必要なときに同じインスタンスを作成するのではなく、既存のインスタンスを使用します.
なぜSingleton Patternを使うのか
①固定メモリ領域を取得しながら、新しいインスタンスを使用することで、メモリの無駄を防止できます.
②モノトーンのクラスインスタンスはグローバルインスタンスであり、他のクラスのインスタンスはデータを共有しやすい.
使用のために複数の共通オブジェクト(Ex.DB Managerなど)を作成する必要がある場合に使用します.
③Androidアプリでは、アクティビティやクラスの主要クラスごとに一つひとつ伝えにくいため、単色調に設計されており、随所にアクセスしやすい.
すなわち、インスタンスが絶対に1つしか存在しないことを保証したい場合に使用できます.
Singleton Patternの質問
マルチスレッド環境で同期処理を行わない場合、2つのインスタンスが生成される場合があります.
Singleton Patternのタイプ
(1)Eager Initialization(初期化)
public class TestMaanger {
   // private static으로 선언하기
   private static TestManager instance = new TestManager();

   // 생성자
   private TestMaanger() { }

   // 인스턴스 리턴하기
   public static TestManager getInstance() {
     return instance;
   }
}
これはモノトーンモードの最も基本的なタイプのEager Initialization方式です.
まず、クラスでグローバル変数を使用してインスタンス変数を作成し、private staticを使用してインスタンスアクセスを行い、privateアクセス制御キーワードを使用してTestManagerを行います.インスタンスへの直接アクセスは許可されていません.
また、コンストラクション関数にprivateアクセス制御キーワードを追加し、他のクラスでnew TestManager()を使用します.新しいインスタンスが何らかの方法で作成されないようにします.
静的メソッドgetInstance()のみを使用してインスタンスにアクセスし、同じインスタンスを使用する基本的な単一トーンの原則を遵守します.
Eager Initializationは、あらかじめモノトーンオブジェクトを生成する方式である.
その利点は、静的に生成された変数にモノトーンオブジェクトを宣言するため、クラスローダがクラスをロードするとモノトーンオブジェクトが生成されることである.
また,クラスローダはクラスの最初のローディング時にオブジェクトを生成するので信頼できる.
欠点は、シングルトーンオブジェクトを使用するかどうかにかかわらず、クラスのロード時にシングルトーンオブジェクトが作成され、メモリが消費されるため、効率が低下することです.
(2)Lazy初期化(遅延初期化)
public class TestManager {
   // private static으로 선언하기
   private static TestManager instance;

   // 생성자
   private TestManager() { }

   // 인스턴스 리턴하기
   public static TestManager getInstance() {
      if(instance == null) {
         instance = new TestManager();
      }
      return instance;
   }
}
Eager Initializationとは対照的に、クラスをロードするのではなくクラスインスタンスを使用するときに、モノトーンインスタンスを作成します.
つまり、モノトーンオブジェクトの作成を使用ポイントに遅らせるため、使用前にメモリが消費されません.
単一トーンオブジェクトが必要な場合にインスタンスを取得し、メモリの漏洩を防ぐことができるという利点があります.
(Eager Initializationメソッドの不足を補う)
欠点は、マルチスレッド環境でgetInstance()を複数の場所から同時に呼び出すと、2回のインスタンスを生成できることです.すなわち,マルチスレッド環境では,単色調哲学が破られる可能性がある.
(3)Thread safe Lazy初期化(スレッド安全の遅延初期化)
public class TestManager {
   // private static으로 선언하기
   private static TestManager instance;

   // 생성자
   private TestManager() { }

   // 인스턴스 리턴하기
   public static synchronized TestManager getInstance() {
      if (instance == null) {
          instance = new TestManager(0;
      }
      return instance;
   }
}
Lazy InitializationがThread-Safeを備えていないという欠点を補うために,同期キーワードを用いてマルチスレッド環境におけるスレッド同時アクセスの同期性の問題を解決する.
利点はLazy Initialization方式がThread-Safeの不足を補うことである.
欠点はsynchronizedキーワードを使用すると、Java内部で領域またはメソッドがロックまたはロック解除され、内部で多くのコストが発生することです.
したがって、多くのスレッドがgetInstanceを呼び出すと、プログラム全体のパフォーマンスが低下します.
(4)Thread safe Lzy初期化+ダブルチェックロック
public class TestManager {
   // private static으로 선언하기
   private static TestManager instance;

   // 생성자
   private TestManager() { }

   // 인스턴스 리턴하기
   public static TestManager getInstance() {
      if (instance == null) {
          synchronized (TestManager.class) {
              if (instance == null) {
                  instance = new TestManager();
              }
          }
      }
      return instance;
   }
}
Thread safe Lzy初期化メソッドは、多くのスレッドが同期メソッドに同時にアクセスするとパフォーマンスが低下します.
この問題を緩和するためにDouble-Checked locking技術を用いた.
インスタンスが最初のif文でnullの場合、synchronized blockにアクセスし、if文を再使用してインスタンスがnullであるかどうかを確認します.
インスタンスは、2つのインスタンスがnullの場合にのみnewでインスタンス化されます.
その後、instanceはnullではないのでsynchronized blockには乗れません.
この二重検査ロック技術は性能の低下を補うことができる.
(5)オンデマンド初期化holder習語(holderによる初期化)
public class TestManager {
   // 생성자
   private TestManager() { }
   
   // 인스턴스를 리턴할 Holder 클래스 생성하기
   private static class TestManagerHolder {
      private static final TestManager instance = new TestManager();
   }

   public static TestManager getInstance() {
      return TestManagerHolder.instance;
   }
}
これはクラスにクラス(Holder)を配置し,JVMのクラスローディングメカニズムとクラスの時点を利用する方法である.
Lazy Initialization方式により,Thread間の同期問題を同時に解決できる.
ネストされたクラスHolderはgetInstance()メソッドを呼び出す前に参照されず、getInstanceメソッドを呼び出すときにクラスローダによって単一トーンオブジェクトを作成して返されます.
Holderで宣言されたインスタンスは静的であるため、クラスのロード時に一度だけ呼び出されることを知っておく必要があります.またfinalを使用して値の再割り当てを回避します.
これはこれまでで最も多く使われてきた方法です.