싱글톤 패턴
인스턴스를 오직 한개만 제공하는 클래스
환경 세팅에 대한 정보 등, 인스턴스가 여러 개 일 때 문제가 생길 수 있는 경우가 있다.
인스턴스를 오직 한개만 만들어 제공하는 클래스가 필요하다
•
게임에 대한 설정
핵심
•
private 생성자 + static한 생성자
public class Settings(){
private static Settings instance;
private Settings(){}
public static Settings getInstance(){
if(instance == null){
return new Settings();
}
return instance;
}
}
Java
복사
→ 이 방법은 쓰레드에 안전하지 않다.
왜냐?
생성되는 순간에 instance를 평가한다면, 아직 만들어지지 않았기 때문에 또 만들어진다.
그러면 가장 쉬운 방법은 getInstance에 synchronized라는 키워드를 만들자.
public class Settings(){
private static Settings instance;
private Settings(){}
public static synchronized Settings getInstance(){
if(instance == null){
return new Settings();
}
return instance;
}
}
Java
복사
→ 이러면 성능에 무리가 올 수 있다. lock을 걸기 때문에
뿐만 아니라 eager initialization을 이용하자. if 객체 생성 비용이 크지 않을 때
미리 만드는게 단점, 즉 생성할 때 꽤 시간이 걸리고 메모리를 많이 먹는다면, 이라면 이러면 좀...
그러면 double checked locking을 하자.
public class Settings(){
private static volatile Settings instance;
private Settings(){}
public static Settings getInstance(){
if(instance == null){
synchronized(Settings.class){
if(instance == null){
instance = new Settings();
}
}
}
return instance;
}
}
Java
복사
→ getInstance()를 매번 호출할 때마다, synchronized를 쓰진 않는다.
인스턴스가 만약에 있으면 그냥 instance를 return하기 때문
굉장히 복잡한 방법이다.
→ 단순하게 안전하게 구현하자!
static inner 클래스 사용하기
public class Settings(){
private Settings(){ }
private static class SettingsHolder{
private static final Settings INSTANCE = new SETTINGS();
}
public static Settings getInstance(){
return SettingsHolder.INSTANCE;
}
}
Java
복사
lazy 로딩이 가능하다.
멀티쓰레드에서 안전하다.
⇒ static한 메서드를 통해 생성하고, static한 필드를 이용하기 때문에
싱글톤 깨트리기~
1.
리플렉션을 사용하자 ⇒ 리플렉션을 사용해서 private한 메서드에 접근해서 생성자를 호출하자
2.
직렬화 & 역직렬화, implements Serializable ⇒ 역직렬화를 하면 반드시 생성자를 다시 호출하기 때문에 다른 객체가 된다.
Enum을 쓰자
Enum 같은 경우도 프로퍼티와 메소드를 정의할 수 있기 때문에 써도 된다.
⇒ 리플렉션에 안전한 코드가 된다.
어디에 쓰이나?
1.
Java의 Runtime이라는 인스턴스
a.
⇒ 미리 만들어놨기 때문에 쓰레드 safe하다.
b.
자바 앱이 실행되고 있는 실행환경에 대한 얘기
2.
스프링에서는 ApplicationContext
3.
다른 디자인 패턴의 구현체 일부
팩토리 메소드 패턴
•
구체적으로 어떤 인스턴스를 만들지는 서브 클래스가 정한다.
•
인스턴스를 생성하는 책임을 구체적인 클래스가 아니라 추상적인 인스턴스의 메서드로 감싸는 것이다.
Creator, Product라는 인터페이스를 일단 만들고
거기서 ConcreteCreator를 구현하고 거기서 ConcreteProduct를 생산한다.
기존에 if-else로 객체가 생성되는 경우, 팩토리가 변경에 대해서 닫혀있지 않다.
요구 사항에 대해서 계속 바뀌기 때문
⇒ 근데 클라이언트 코드는 바뀌지 않느냐..? 바뀐거 아님?
⇒ 맞다 하지만, 의존성 주입 위주로 하면 클라이언트 코드도 최소한으로 줄일 수 있음
예시들
1.
JAVA : Calendar, NumberFormat
2.
Spring : BeanFactory → CreatorInterface이다.
a.
이거에 해당하는 여러 구현체들이 있다. ClassPathXmlApplicationContext 등등