2022. 1. 26. 23:53ㆍETC/Design Patterns
Object Creational Pattern
Singleton Pattern
----------------- INDEX -----------------
Singleton Pattern ?
Structure
Sample Code: Java
Race Condition
관련 패턴
----------------------------------------------
Ensure a class only has one instance, and provide a global point of access to it.
- GoF Design Patterns
싱글턴 패턴은 클래스 당 인스턴스를 하나만 존재하게 하고, 전역적인 접근 지점을 제공합니다.
프린터를 통해 문서를 출력한다고 해봅시다.
서로 다른 문서를 출력할 때마다 새로운 프린터를 하나씩 구비해야 한다면.. 큰 비용낭비가 되겠죠?
하나의 객체만을 사용해서 여기저기서 사용하면 효율적일테니까요.
싱글턴 패턴은 바로 이런 점에서 유용합니다.
단 하나의 인스턴스를 생성하고 전역적으로 공유합니다.
싱글턴 패턴을 제작하기 위한 효과적인 방법은 싱글턴 패턴 생성의 책임을 싱글턴 패턴 스스로에게 주는 것입니다.
어떻게 관리하는지 아래 예시를 볼까요?
Structure
Singleton
클라이언트가 고유한 인스턴스에 액세스할 수 있는 메서드를 정의합니다.
자체 객체를 생성하는 책임을 가질 수 있습니다.
- instance: Singleton
싱글톤 패턴은 내부적으로 하나의 객체만 보장하기 위해 자체 객체를 저장하는 참조체(reference)를 갖고 있어서, 참조체를 통해 자신의 객체가 생성되었는지 판단합니다.
+ getInstance: Singleton
위의 그림에서 instance
라는 속성으로 자체 객체를 참고하며, getInstance
를 통해 자체 객체를 반환합니다.
이 과정에서 자체 객체가 생성되어 있는지를 확인하죠.
만약 존재하지 않는다면 새로 생성하고, 존재한다면 더 이상 생성하지 않고 생성되어있는 객체를 반환합니다.
이 과정을 통해 단 하나의 인스턴스를 사용함을 보장할 수 있습니다.
- Singleton()
단 하나의 인스턴스를 보장하기 위해서는 짚고 가야할 부분이 있습니다.
바로, new
를 막아야 합니다.
생성자를 private으로 설정하게 되면 새로운 인스턴스 생성을 막을 수 있습니다.
싱글턴 패턴에서 이 부분을 놓친다면 실수를 발생시킬 수 있습니다.
그렇다면 어떻게 이 싱글턴 객체를 외부에서 접근하게 만들까요?
바로 static을 사용합니다.
Static
Static 키워드는 변수와 메서드에 적용할 수 있습니다.
Static 키워드를 통해 선언된 멤버는 선언만으로 메모리에 할당되어 상주하게 됩니다.
메모리 할당을 딱 한번만 하게 되어 메모리 사용에 이점이 있습니다.
또, Static 키워드를 통해 정적 멤버들을 생성하면 Heap영역이 아닌 Static 영역에 할당됩니다.
같은 곳의 메모리 주소만을 바라보게 되어서 전역적으로 Static 변수의 값을 공유할 수 있는 것입니다.
즉, Static 영역에 할당된 메모리는 어디서든지 참조할 수 있다는 특징이 있죠.
이러한 Static 의 특징으로 싱글턴을 생성합니다.
코드를 확인해보면 Singleton 패턴을 완전히 이해할 수 있을테니 확인해볼까요?
Sample Code: Java
Singleton
public class Printer {
private static Printer printer = null;
private Printer() { }
public static Printer getPrinter(){
if (printer == null) {
printer = new Printer();
}
return printer;
}
}
Client
public class Client {
public static void main(String[] args) {
Printer printer1 = Printer.getInstance();
Printer printer2 = Printer.getInstance();
System.out.println(printer1 == printer2);
}
}
결과는 true가 출력됩니다.
Race Condition
= 경합조건
싱글턴 패턴을 사용할 때의 핵심은 인스턴스를 하나만 생성해야한다는 점인데요.
멀티스레드 환경에서는 이 조건을 위반할 가능성이 있습니다.
예를 들어, thread-1와 thread-2 스레드가 존재한다고 가정해봅시다.
thread-1 이 getInstance
의 if문 내로 들어가 생성하기 직전에, thread-2 가 if문을 통과할 수 있습니다.
두 개의 싱글턴이 생성될 수 있다는 문제점이 받아들여지시나요?
해결 방법은 두 가지로 간단합니다.
첫 번째는 인스턴스를 static으로 미리 만들어 둡니다.
public class Printer {
private static Printer printer = new Printer();
private Printer() { }
public static Printer getInstance(){
return printer;
}
}
단, Printer를 사용하지 않을 때도 생성된다는 점을 주의하세요.
두 번째로는 getInstance
를 동기 처리합니다.
package singleton;
public class Printer {
private static Printer printer = null;
private Printer() { }
public synchronized static Printer getInstance(){
if (printer == null) {
printer = new Printer();
}
return printer;
}
}
관련 패턴
C- Creational Patterns | S - Structural Patterns | B - Behavioral Patterns
추상 팩토리 채턴은 객체를 생성하는 패턴입니다. 이 패턴을 사용할 때, 구체화된 하나의 객체만을 생성할 수 있는데, 이때 싱글턴 패턴을 같이 적용해 족합적인 생성패턴을 설계할 수 있습니다.
빌더 패턴은 의존성있는 객체를 싱글턴으로 생성할 때 사용합니다.
관계에 따라 의존되는 객체의 생성 순서가 있을 수 있으며,
빌더는 생성 순서와 단계, 복합적인 절차 등을 다룹니다.
퍼사드 패턴은 외부에서 객체에 접근할 수 있는 단일 창구와 같은 개념입니다.
외부와 통신하는 객체를 하나만 만들면 효율적일 대, 단일화된 접속 지점으로 설계할 수 있습니다.
그럼 지금까지 Singleton Pattern에 대해 알아보았습니다.
오타나 잘못된 내용이 있다면 댓글로 남겨주세요!
감사합니다 ☺️
'ETC > Design Patterns' 카테고리의 다른 글
Design Pattern, Decorator (0) | 2022.02.03 |
---|---|
Design Pattern, Composite (0) | 2022.02.01 |
Design Pattern, Mediator (0) | 2022.01.24 |
Design Pattern, Adapter (0) | 2022.01.23 |
Design Pattern, Observer (0) | 2022.01.21 |
Backend Software Engineer
𝐒𝐮𝐧 · 𝙂𝙮𝙚𝙤𝙣𝙜𝙨𝙪𝙣 𝙋𝙖𝙧𝙠