본문 바로가기
프로그래밍/디자인패턴

싱글톤(Singleton) 패턴

by bantomak 2024. 4. 30.

🤔문제

싱글턴 패턴은 한 번에 두 가지의 문제를 동시에 해결함으로써 단일 책임 원칙을 위반한다.

  1. 클래스에 인스턴스가 하나만 있도록 한다. 사람들은 클래스에 있는 인스턴스 수를 제어하려는 가장 일반적인 이유는 일부 공유 리소스(예: 데이터베이스 또는 파일)에 대한 접근을 제어하기 위함이다.

    예를 들어 객체를 생성했지만 잠시 후 새 객체를 생성하기로 했다고 가정해 봅시다. 그러면 새 객체를 생성하는 대신 이미 만든 객체를 받게 된다.

  2. 해당 인스턴스에 대한 전역 접근 지점을 제공한다. 필수 객체들을 저장하기 위해 전역 변수들을 정의했다고 가정해 보자. 이 변수들을 사용하면 매우 편리할지는 몰라도, 모든 코드가 잠재적으로 해당 변수의 내용을 덮어쓸 수 있고 그로 인해 앱에 오류가 발생해 충돌할 수 있으므로 그리 안전한 방법은 아니다.

    전역 변수와 마찬가지로 싱글턴 패턴을 사용하면 프로그램의 모든 곳에서부터 일부 객체에 접근할 수 있다. 그러나 이 패턴은 다른 코드가 해당 인스턴스를 덮어쓰지 못하도록 보호하기도 한다.

😎해결책

싱글턴의 모든 구현은 공통적으로 다음의 두 단계를 갖는다.

  • 다른 객체들이 싱글턴 클래스와 함께 new 연산자를 사용하지 못하도록 디폴트 생성자를 비공개로 설정
  • 생성자 역할을 하는 정적 생성 메서드를 만들자. 내부적으로 이 메서드는 객체를 만들기 위하여 비공개 생성자를 호출한 후 객체를 정적 필드에 저장한다. 이 메서드에 대한 그다음 호출들은 모두 캐시 된 객체를 반환한다.

싱글턴 클래스에 접근할 수 있는 경우, 이 코드는 싱글턴 정적 메서드를 호출할 수 있다. 따라서 해당 메서드가 호출될 때마다 항상 같은 객체가 반환된다.

 

🏠구조

구현 방법

  1. 싱글턴 인스턴스의 저장을 위해 클래스에 비공개 정적 필드를 추가한다.
  2. 싱글턴 인스턴스를 가져오기 위한 공개된 정적 생성 메서드를 선언하자.
  3. 정적 메서드 내에서 '지연된 초기화'를 구현하자. 그러면 이것은 첫 번째 호출에서 새 객체를 만든 후 그 객체를 정적 필드에 넣을 것이다. 이 메서드는 모든 후속 호출들에서 항상 해당 인스턴스를 반환해야 한다.
  4. 클래스의 생성자를 비공개로 만들자. 그러면 클래스의 정적 메서드는 여전히 생성자를 호출할 수 있지만 다른 객체들은 호출할 수 없을 것이다.
  5. 클라이언트 코드를 살펴보며 싱글턴의 생성자에 대한 모든 직접 호출들을 싱글턴의 정적 생성 메서드에 대한 호출로 바꾸자.

👍장단점

  • 클래스가 하나의 인스턴스만 갖는다는 것을 확신할 수 있다.
  • 이 인스턴스에 대한 전역 접근 지점을 얻는다.
  • 싱글턴 객체는 처음 요청될 때만 초기화 및 생성된다.
  • 단일 책임 원칙을 위반한다. 이 패턴은 한 번에 두가지의 문제를 동시에 해결한다. (하나의 인스턴스, 접근지점 제공)
  • 또 싱글턴 패턴은 잘못된 디자인(예를 들어 프로그램의 컴포넌트들이 서로에 대해 너무 많이 알고 있는 경우)을 가릴 수 있다.
  • 다중 스레드 환경에서 여러 스레드가 싱글턴 객체를 여러 번 생성하지 않도록 특별한 처리가 필요하다.
  • 싱글턴의 클라이언트 코드를 유닛 테스트하기 어려울 수 있다. 많은 테스트 프레임워크들이 모의 객체들을 생성할 때 상속에 의존하기 때문이다. 싱글턴 클래스의 생성자는 비공개이고 대부분 언어에서 정적 메서드를 오버라이딩하는 것이 불가능하므로 싱글턴의 한계를 극복할 수 있는 창의적인 방법을 생각해야 한다. 아니면 그냥 테스트를 작성하지 말거나 싱글턴 패턴을 사용하지 않으면 된다.

🔍다른 패턴과의 관계

  • 대부분의 경우 하나의 퍼사드 객체만 있어도 충분하므로 퍼사드 패턴의 클래스는 종종 싱글턴으로 변환될 수 있다.
  • 만약 객체들의 공유된 상태들을 단 하나의 플라이웨이트 객체로 줄일 수 있다면 플라이웨이트싱글턴과 유사해질 수 있다. 그러나 이 패턴들에는 두 가지 근본적인 차이가 있다.
    1. 싱글턴은 인스턴스가 하나만 있어야 한다. 반면에 플라이웨이트 클래스는 여러 고유한 상태를 가진 여러 인스턴스를 포함할 수 있다.
    2. 싱글턴 객체는 변할 수 있다(mutable). 플라이웨이트 객체들은 변할 수 없다(immutable).
  • 추상 팩토리들, 빌더들 및 프로토타입들은 모두 싱글턴으로 구현할 수 있다.

'프로그래밍 > 디자인패턴' 카테고리의 다른 글

프로토타입(Prototype) 패턴  (1) 2024.04.30
빌더(Builder) 패턴  (1) 2024.04.24
추상 팩토리(Abstract Factory) 패턴  (1) 2024.04.24
팩토리 메서드(Factory Method) 패턴  (0) 2024.04.23
OOP의 기초  (1) 2024.04.19

댓글