티스토리 뷰

JAVA

싱글톤은 Enum 타입으로 만들어라

세댕댕이 2022. 7. 15. 01:30

3. private 생성자나 enum 자료형은 싱글톤 패턴을 따르도록 해라

(중요) 여기서의 싱글톤 패턴은 스프링 컨테이너에 의해 관리되는 싱글톤이 아니기에, 안티패턴의 문제점을 갖고있는 싱글톤임을 참고!!

 

# 싱글톤 패턴이 안티패턴으로 불리는 이유 =========

1. private 생성자를 갖고있어 상속 불가

2. 테스트 하기 어려움 (목 객체로 대체할 수 없음)

3. 객체지향의 의도와 전혀 맞지않음

 

=> 위 문제점들은 스프링에서는 "스프링 컨테이너"의 활약으로 모두 해결된다

==========================================

 

싱글톤은 생성자를 private로 선언해두고, 싱글톤 객체는 정적 팩토리 메서드( getInstance() )를 이용하거나, 정적 필드를 통해 얻어오게 된다.

public class User {
    private static final User USER = new User();
    private User() {}
    public static User getInstance() {return USER;}
}

 

위와 같은 기존 방법으로 싱글턴 클래스를 직렬화 가능하게 하려면, 클래스가 단순히 Serializable 인터페이스를 구현하는 것만으로는 불가능하다

- 역직렬화 할 때마다 새로운 객체가 생성되어 싱글턴이 깨져버린다.

- 이를 방지하기 위해서는 모든 필드를 transient로 선언하고 readResolve 메서드를 추가하는 등 귀찮은 작업이 필요하다.

 

그래서 어쩌라고?

=> 싱글턴을 사용할 거면 Enum 타입으로 싱글턴을 만들어라!!

Enum은 태생부터 싱글턴이 보장되도록 만들어졌다. (Enum이 == 비교가 가능한 이유는 싱글턴이기 때문)

싱글턴 객체를 만들 때 사용하지 않을 이유가 없다... 고 하는데 이거 진짜 쓰나?? 모르겠다.

public enum User {
    INSTANCE;

    public String method() {
        return "Hello World";
    }
    ...
}
////////////
public static void main(String[] args) {
    User user = User.INSTANCE;
    System.out.println(user.method());
}

동작은 잘 하는데. 맞는건지는 잘 모르겠다. Enum에 익숙하지 않아서 그런건지도 모르겠다.

아무튼 싱글턴을 Enum으로 만들면 직렬화 문제도 해결되고, 리플렉션을 통한 강제 싱글턴 생성 공격도 방지할 수 있고 장점이 있다고 한다.

 

근데 스프링 빈이 아니고서야 의도적으로 자바 싱글턴 객체를 만들 일은 거의 드물 것 같다.. 몰루

 

(+) 자바의 직렬화와 역직렬화 ============

직렬화(Serialization): 객체를 직렬화하여 연속적인 데이터로 변환하는 것.

- 객체를 파일로 저장하거나, 전송하고자 할 때 사용된다.

(= 객체 또는 데이터를 외부의 시스템에서도 사용할 수 있도록 변환하는 기술.)

- ObjectOutputStream 클래스의 writeObject() 메서드를 이용

 

역직렬화(Deserialization): 직렬화된 연속적인 데이터를 객체로 복원하는 것.

- 저장된 파일을 읽거나, 전송받은 스트림 데이터를 읽을 때 사용된다.

- ObjectInputStream 클래스의 readObject() 메서드를 이용.

- 직렬화된 객체의 클래스가 클래스 패스(Path)에 존재해야 하고, import된 상태여야 역직렬화가 가능하다

 

* 데이터는 값 형식 데이터(int, char, double 등, 스택 메모리)와 참조 형식 데이터(주소값, 힙 메모리)로 구분된다.

- 이때, 객체를 파일로 저장하거나 통신 할 때에는, 참조형식의 데이터를 사용할 수 없다. 참조형식 데이터는 프로그램이 종료되고 나면 그대로 사라지고, 나중에 재시작 되더라도 동일 주소에 동일한 객체가 재생성 되는것을 보장하지 않는다.

-> 따라서 직렬화 과정을 통해 참조 형식의 데이터를 싹 끌어모아서 값 형식 데이터로 모두 변환시키는 것.

 

* 직렬화의 종류: CSV/XML/JSON 직렬화, Binary 직렬화, Java 직렬화

 

* 자바에서 직렬화를 위해서는 java.io.Serializable 인터페이스를 구현해야 한다.

- 구현 메소드 X. 직렬화를 명시하는 마크 인터페이스 용도.

- 객체가 다른 객체를 멤버변수로 가지고 있는 경우, 멤버 모두 직렬화 가능한 클래스여야 함!!

 

* 직렬화 대상 객체는 동일한 serialVersionUID를 가지고 있어야 한다.

- 별도로 선언하지 않았다면, 클래스의 구조 정보를 이용해 자동으로 해시값을 할당해주나, 이 경우 클래스의 멤버변수가 변경되면 UID도 변경되게 된다

- 직렬화 가능한 객체라면 serialVersionUID 멤버변수를 따로 만들어서 직접 관리하는 것이 좋다

(private static final으로 지정)

- 직렬화 시에 포함을 원치 않는 멤버 변수가 있다면 변수 접근 제한자 뒤에 "transient"를 붙여주면 된다

======================================

 

 

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함