(item 3) private 생성자나 열거타입으로 싱글턴임을 보증하라

ynkim·2023년 11월 17일
0

싱글턴

  • 인스턴스를 오직 하나만 생성할 수 있는 클래스
  • 클래스를 싱글턴으로 만들면 테스트하기 어려워질 수 있음. 인터페이스 구현체가 아닌 싱글턴은 싱글턴 인스턴스를 mock 구현으로 대체할 수 없기 때문

public static final 필드 방식

  • 클래스가 싱글턴임이 api에 명백히 드러남
  • 간결함
public class Elvis {
    public static final Elvis INSTANCE = new Elvis();

    private Elvis() { }

    public void leaveTheBuilding() {
        System.out.println("Whoa baby, I'm outta here!");
    }

    // This code would normally appear outside the class!
    public static void main(String[] args) {
        Elvis elvis = Elvis.INSTANCE;
        elvis.leaveTheBuilding();
    }
}

정적 팩터리 메서드를 public static 멤버로 제공

  • api를 바꾸지않고 싱글턴이 아니게 변경 가능
  • 정적 팩터리를 제너릭 싱글턴 팩터리로 만들 수 있다
  • 정적 팩터리의 메서드 참조를 공급자로 사용할 수 있다 Elvis::getInstance
public class Elvis {
    private static final Elvis INSTANCE = new Elvis();
    private Elvis() { }
    public static Elvis getInstance() { return INSTANCE; }

    public void leaveTheBuilding() {
        System.out.println("Whoa baby, I'm outta here!");
    }

    // This code would normally appear outside the class!
    public static void main(String[] args) {
        Elvis elvis = Elvis.getInstance();
        elvis.leaveTheBuilding();
    }
}
public class Elvis {
    private Elvis() { }
    
    public static Elvis getInstance() { 
    	return LazyHolder.INSTANCE; 
    }

    private static class LazyHolder {
    	private static final Elvis INSTANCE = new Elivs();
    }
}

원소가 하나인 열거타입을 선언

  • 추가 노력없이 직렬화 가능
  • Enum 외의 클래스를 상속할 수 없다
public enum Elvis {
    INSTANCE;

    public void leaveTheBuilding() {
        System.out.println("Whoa baby, I'm outta here!");
    }

    // This code would normally appear outside the class!
    public static void main(String[] args) {
        Elvis elvis = Elvis.INSTANCE;
        elvis.leaveTheBuilding();
    }
}

싱글턴 클래스 직렬화

  • 단순히 Serializable을 구현하는 것으로는 부족
  • 모든 인스턴스 필드를 transient로 선언하고 readResolve 메서드를 제공해야 인스턴스를 역직렬화 할 때마다 새로운 인스턴스가 만들어지지 않는다
public class Elvis implements Serializable {
    public static final Elvis INSTANCE = new Elvis();
	// transient public static final Elvis INSTANCE = new Elvis();
    
    private Elvis() { }

    public Object readResolve(){
        return this.INSTANCE;
    }
}

제네릭 싱글턴 팩터리

public class GenericSingletonFactory {
    // Generic singleton factory pattern
    private static UnaryOperator<Object> IDENTITY_FN = (t) -> t;

    @SuppressWarnings("unchecked")
    public static <T> UnaryOperator<T> identityFunction() {
        return (UnaryOperator<T>) IDENTITY_FN;
    }

    // Sample program to exercise generic singleton
    public static void main(String[] args) {
        String[] strings = { "jute", "hemp", "nylon" };
        UnaryOperator<String> sameString = identityFunction();
        for (String s : strings)
            System.out.println(sameString.apply(s));

        Number[] numbers = { 1, 2.0, 3L };
        UnaryOperator<Number> sameNumber = identityFunction();
        for (Number n : numbers)
            System.out.println(sameNumber.apply(n));
    }
}

0개의 댓글