[이펙티브 자바] 아이템 3. private 생성자나 열거 타입으로 싱글톤임을 보증하라.

2 minute read

이펙티브 자바를 공부한 내용입니다.


private 생성자나 열거 타입으로 싱글톤임을 보증하라

싱글톤이란 인스턴스를 오직 하나만 생성하여 사용하는 것을 말한다.
보통 싱글톤 패턴을 사용해 싱글톤 클래스를 만든다.


싱글톤 패턴을 만드는 방법


싱글톤 패턴을 만드는 방법 첫번째

싱글톤 패턴을 만드는 방식은 크게 두가지가 있다.
첫번째 방법은 생성자를 private으로 감추고, 인스턴스에 접근할 수 있는 상수를 final로 선언하는 것이다.

public class Singletone {
    
    private Singletone() {}

    public static final Singletone INSTANCE = new Singletone();

}

이러한 방법을 통해 싱글톤 클래스를 만들면 생성자의 접근제어자가 private이기 때문에
new 키워드를 통해 객체를 생성하지 못한다.
그러므로 최초 Singletone 클래스가 초기화 될 때 만들어진 INSTANCE 상수가
전체 시스템 중 유일한 객체이다.

그러나 자바에는 리플렉션이라는 API가 존재하는데,
AccessibleObject.setAccessible을 true를 주게 되면 private 생성자도 호출 할 수 있게 되어 유일한 객체임을 보장받지 못한다.


싱글톤 패턴을 만드는 방법 두번째

두번째 방법은 정적 팩토리 메서드를 제공하는 것이다.

public class Singletone {
    
    private Singletone() {}

    private static final Singletone INSTANCE = new Singletone();


    public static Singletone getInstance() {
        return INSTANCE;
    }

}

첫번째 방법과 마찬가지로 생성자는 private이고, 상수를 만든다.
이때 상수는 private으로 정적 팩토리 메서드인 getInstance를 통해서만 호출 가능하다.

이러한 방법으로 싱글톤 클래스를 생성한다면 역시 INSTANCE는 유일한 객체이지만,
리플렉션을 통해 private 생성자를 호출 할 수 있어 유일한 객체임을 보장받지는 못한다.


싱글톤 패턴을 만드는 방법 세번째

세번째 방법은 enum을 활용하는 것이다.
enum은 Enumeration의 약자로 상수를 열거한 집합이다.
보통 상수를 표현할 때 final 키워드를 사용한 변수들을 클래스에서 사용하는데,
JDK 1.5 이후 enum이 추가되었다.
enum은 가독성이 좋고, 열거라는 의도가 뚜렷하기 때문에
enum을 사용하는 것이 더욱 효과적이라고 생각한다.

클래스를 사용한 상수 열거

public class CommonStatus {
    
    public static final String IS_OK = "200";

    public static final String BAD_REQUEST = "400";

}

enum을 사용한 상수 열거

public enum CommonStatus {

    IS_OK, BAD_REQUEST
    
}

이러한 enum은 인스턴스 생성을 할 수 없다는 특징이 있다.

error

리플렉션을 통해서 newInstance 메서드를 호출하거나,
getConstructor 메서드를 호출해 생성자를 만들고,
setAccessible을 true로 만들어주더라도 객체를 생성하지 못한다.

error

error

이렇게 완전한 싱글톤 객체를 만들 수 있다.


enum을 통해 만든 싱글톤의 문제점

만약 만드려고 하는 싱글톤이 enum 외 다른 클래스를 상속하고자 한다면, 사용할 수 없다.

error

단 enum 역시 인터페이스는 구현할 수 있다.

public interface Operator {

    double apply(double x, double y);

}
public enum Calculator implements Operator {

    PLUS {
        @Override
        public double apply(double x, double y) {
            return x + y;
        }
    },

    MINUS {
        @Override
        public double apply(double x, double y) {
            return x - y;
        }
    }

}
public class EnumTest {

    public static void main(String[] args) {
        System.out.println(Calculator.PLUS.apply(1, 2));
        System.out.println(Calculator.MINUS.apply(100, 4));
    }

}

// 3.0
// 96.0

사실 인터페이스 구현이 가능한 것은 전혀 몰랐던 부분인데,
상황에 맞게 잘 사용한다면 좋은 구현방법이 될 것 같다.


참조자료

Java Enum
https://www.nextree.co.kr/p11686/

Enum 인터페이스 구현 예제
https://wedul.site/289

Tags:

Categories:

Updated:

Leave a comment