[物品3]プライベートジェネレータまたは列挙タイプを使用して単輪を保証する


たんりん

  • シングルターン(singleton)とは、1つのクラスしか生成できないことを意味します.

  • 1つの典型的な一輪例は、無状態オブジェクト(例えば、関数)または設計において一意でなければならないシステム要素であってもよい.
  • ステータスオブジェクトなしと設計で唯一のシステムコンポーネント?
    ステータスオブジェクトなし
    クラスにインスタンス変数がないオブジェクトを単純に表示
    したがって、このオブジェクトは、特定のクライアントのステータスに依存しているか、フィールドの値の変更が許可されていないか、読み取り専用のステータスが必要な場合があります.
    たとえば、次のように表示される場合があります.したがって、関数などのステータスレスオブジェクトと呼ばれます.
    class Stateless {
        void test() {
            System.out.println("Test!");
        }
    }
    class Stateless {
        //No static modifier because we're talking about the object itself
        final String TEST = "Test!";
    
        void test() {
            System.out.println(TEST);
        }
    }
    このように書かれた利点は、スレッドのセキュリティであり、例えば、受注金額を処理する論理であり、例えば、Aから10000元、Bから20000元と処理されると、この金額変数は変化し続けるため、この場合、このような問題を単一のポイントでステータスレスオブジェクトを使用して処理することができる.
    設計で唯一のシステム構成部品
    本の例では、privateジェネレータとしてのpublic static finalフィールドElvis.INSTANCEの初期化では1回のみ呼び出されます.これは、インスタンスがシステム全体で唯一保証されているシステム要素であることを意味します.

  • ここで、クラスを単一のループに設定すると、クラスを使用するクライアントがテストしにくくなる可能性があります.

  • インタフェースによって作成された単一インスタンスを実装するのではなく、タイプをインタフェースとして定義する場合、単一インスタンスを「偽」(mock)実装で置き換えることはできません.

  • 単一インスタンスを作成する方法は、通常、どちらの方法も作成者をprivateとして非表示にし、public staticメンバーを唯一のアクセス可能なインスタンスとして提供する方法です.

  • public staticメンバーがfinalフィールドである方法は以下の通りである(上記設計で唯一のシステムコンポーネントと考えられる)
  • public class Elvis {
    		public static final Elvis INSTANCE = new Elvis();
    		private Elvis() { ... }
    
    		public void leaveTheBuilding() { ... }
    }

  • private作成者はpublic static finalフィールドElvis.INSTANCEを初期化する際に1回のみ呼び出す

  • publicまたはprotected作成者がいないため、Elvisクラスの初期化時に作成されたインスタンスがシステム全体に1つしかないことを確認します.

  • 例外として、認証されたクライアントはReplication APIを使用してプライベートジェネレータを呼び出すことができ、この攻撃を防止するには、ジェネレータを変更して2番目のオブジェクトを作成するときに例外を放出する必要があります.

  • APIは、クラスが単一のインスタンスであり、他のオブジェクトを絶対に参照できないことを明確に示している.
  • APIに返信しますか?
    返信API
    Replication APIはJava APIであり、ユーザーが特定のクラスタイプを知らずにそのクラスの情報(メソッド、タイプ、変数など)にアクセスできるようにします.
    例:
    public class Car {
        private final String name;
        private int position;
    
        public Car(String name, int position) {
            this.name = name;
            this.position = position;
        }
    
        public void move() {
            this.position++;
        }
    
        public int getPosition() {
            return position;
        }
    }
    Reflection APIではなくCar内のメソッドを使用している場合は、Carクラスの特定のタイプが分からないため、エラーが発生します.
    Replication APIを使用して、次のようにCarクラスのmoveメソッドを呼び出すことができます.
    public static void main(String[] args) throws Exception {
        Object obj = new Car("foo", 0);
        Class carClass = Car.class;
        Method move = carClass.getMethod("move");
    
        // move 메서드 실행, invoke(메서드를 실행시킬 객체, 해당 메서드에 넘길 인자)
        move.invoke(obj, null);
    
        Method getPosition = carClass.getMethod("getPosition");
        int position = (int)getPosition.invoke(obj, null);
        System.out.println(position);
        // 출력 결과: 1
    }
    Replication APIにより、以下に示すようにmoveメソッドにアクセスおよび処理できます.
  • パブリック静的メンバーとして静的メソッドを提供する
  • public class Elvis {
    		private static final Elvis INSTANCE = new Elvis();
    		private Elvis() { ... }
    		public static Elvis getInstance() { return INSTANCE; } 
    		
    		public void leaveTheBuilding() { ... }
    }

  • 上記のように処理すると、Elvis.getInstance()常に同じオブジェクトの参照を返しますので、2番目のElvisインスタンスは作成しません

  • APIを変更することなく、単一でないインスタンスに変更できます.また、呼び出したスレッドに基づいてファクトリメソッドに異なるインスタンスを渡すこともできます.

  • 必要に応じて、静的ファクトリをGeneric単一インスタンスファクトリにすることができます.

  • 静的ファクトリのメソッドリファレンスをプロバイダとして使用することもできます.
  • APIも変えず単周も変えず?、ジェニーンリック単反工場?仕入先の使用状況?
    この方法の基礎は静的工場である.
    静的ファクトリ単一インスタンスを取得する方法は、次のとおりです.
    public class YongCoding{
    	
        // 인스턴스를 얻기 위해서는 정적 팩토리 메소드를 이용해야한다.
    	private static final YongCoding INSTANCE = new YongCoding();
        
        // private 생성자
        private YongCoding(){
        // 생략!
    	}
        
        // 정적 팩토리 메소드 방식으로 싱글턴 객체 얻기
       	public static YongCoding getInstance(){
        	return INSTANCE;
        }
    }
    1回の回転ではなく、ここで変更を行う場合は、次の内容を変更して新しいオブジェクトを作成して戻すことができます.
    public class YongCoding{
    	
        // 인스턴스를 얻기 위해서는 정적 팩토리 메소드를 이용해야한다.
    	private static final YongCoding INSTANCE = new YongCoding();
        
        // private 생성자
        private YongCoding(){
        // 생략!
    	}
        
        // 정적 팩토리 메소드 방식으로 싱글턴 객체 얻기
       	public static YongCoding getInstance(){
        	return new YongCoding(); // 변경 부분
        }
    }
    では、ここでAPIを変えずに単輪ではないのは?このクラスを使用する他のクラスを見ると、
    // 시그니쳐의 변경이 없다.
    // 클라이언트는 이 코드가 싱글턴인지 새로운 객체를 생성해서 반환하는지 상관없이 그대로 사용해도 아무 문제가 없다.
    YongCoding.getInstance();
    getInstance()使用上の問題は一切ありません.これは、上記のAPIを変更することなく変更できる方法です
    ジェニーンリック単反工場
    「汎用」を使用してタイプ設定可能なインスタンスを作成し、戻るときに「汎用」を使用して受信したタイプを使用してタイプを決定するタイプです.
    要求されたタイプパラメータに基づいてオブジェクトのタイプを変更するたびに、静的ファクトリを作成します.
    これを「汎用」(General)に設定すると、複数のタイプの内部オブジェクトが受信されてもエラーが発生せず、柔軟性が向上します.
    public class GenericFactoryMethod { 
    public static final Set EMPTY_SET = new HashSet(); 
    
            public static final <T> Set<T> emptySet() { 
                   return (Set<T>) EMPTY_SET; 
            } 
    }
    @Test public void genericTest() { 
            Set<String> set = GenericFactoryMethod.emptySet(); 
            Set<Integer> set2 = GenericFactoryMethod.emptySet(); 
            Set<Elvis> set3 = GenericFactoryMethod.emptySet(); 
    
            set.add("ab"); 
            set2.add(123); 
            set3.add(Elvis.INSTANCE); 
    
            String s = set.toString(); 
            System.out.println("s = " + s); 
    }
    プロバイダ
    この文書では、静的パラメータメソッドリファレンスをプロバイダとして使用できます.このプロバイダをさらに理解している場合は、
    「プロバイダ」(Supplier)はjavaの関数インタフェースであり、すべての抽象メソッドがパラメータを受け入れますが、このSupplierにはパラメータを受け入れず、あるパラメータを返す抽象メソッドがあります.
    SupplierはGenericタイプで、任意のコンテンツを受信して返すことができます.
    例:
    public class Elvis {
        private static final Elvis INSTANCE = new Elvis();
        private Elvis() { }
        public static Elvis getInstance() { return INSTANCE; }
    }
    すなわち、ここではgetInstance()常に同じインスタンスを返し、このメソッドを単に関数として参照する場合にはElvis::getInstanceを用いることができるが、この場合はプロバイダSupplier<Elvis>を用いることができることを意味する.
  • 単一インスタンスクラスをいずれかの方法でシリアル化するには、シリアル化の実施のみを宣言するだけでは不十分であり、すべてのインスタンスフィールドは一時的に宣言しreadResolveメソッドを提供しなければならない.そうしないと、逆シリアル化時に新しいインスタンスが作成される
  • // 싱글턴임을 보장해주는 readResolve 메서드
    private Object readResolve() {
    		// '진짜' Elvis를 반환하고, 가짜 Elvis는 가비지 컬렉터에 맡김
    		return INSTANCE;
    }
    単転レベルシリアル化?
    シングルクラスシリアル化
    名前の通りシリアルインタフェースを実現することを意味します
    しかしここでは逆シリアル化時のような実例角も出現しているため問題が発生し,それを防止するためにはtransient宣言が必要であり,提供readResolve方法transientシリアル化時に除外し、readResolve既存のインスタンスに戻る
    class Class implements Serializable {
          private static final transient Class INSTANCE = new Class();
    
          private Class() { ... }
         
          private Object readResolve() { return INSTANCE; }
    }
  • 単輪を作成する第3の方法は、宣言要素が一体となった列挙タイプ
  • public enum Elvis {
    		INSTANCE;
    		
    		public void leaveTheBuilding() { ... }
    }

  • 簡潔でシリアル化が容易で、非常に複雑なシリアル化状況、およびロールバック攻撃における第2のインスタンスの発生を防止

  • ほとんどの場合、単一のホイールを作成するには、要素の列挙タイプが1つしかありません.

  • 作成する単一インスタンスが非Enumクラスを継承する必要がある場合、このメソッドは使用できません(列挙タイプを宣言して異なるインタフェースを実装できます).
  • 反撃攻撃?
    反撃攻撃
    返信はnon-enum単一ループの2番目のインスタンスを作成および逆シーケンス化できることを示します.
    これにより、クライアントの単輪に対する意味が曖昧になり、それを利用した攻撃とは、攻撃者が開発者が何気なく発生したストリームを作成することによって情報にアクセスまたは取得する攻撃を指す.
    ここではprivateジェネレータ&列挙タイプで上記の問題を防止し、このような攻撃を防止することができます.