[디자인패턴] 싱글턴 패턴(Singleton Pattern)
💡 코드가 보이지 않으시다면 드래그 혹은 오른쪽 아래 🌜 아이콘을 눌러 테마 색을 변경해주세요.
안녕하세요!
키크니 개발자 입니다. 🦒
싱글턴 패턴이란?
어플리케이션이 시작될 때 어떤 클래스가 최초 한번만 메모리를 할당하고(static) 그 메모리에 인스턴스를 만들어 사용하는 디자인입니다.
싱글턴 패턴의 장점은?
- 고정 된 메모리 영역을 얻으면서 한 번의 new로 인스턴스를 사용하기 때문에 메모리 낭비를 방지 할 수 있습니다.
- 싱글턴으로 만들어진 클래스의 인스턴스는 전역이기 때문에 다른 클래스의 인스턴스들이 데이터를 공유하기 쉽습니다.
- 인스턴스가 절대적으로 한 개만 존재하는 것을 보증하고 싶을 경우 사용합니다.
- 두 번째 이용시 부터는 객체 로딩 시간이 줄어 성능이 좋아지는 장점이 있습니다.
싱글턴 패턴의 단점은?
- 싱글턴 인스턴스가 너무 많은 일을 하거나 많은 데이터를 공유시킬 경우에는 다른 클래스의 인스턴스들 간에 결합도가 높아져 "개방-폐쇄 원칙(SOLID의 OCP)"을 위배하게 됩니다.
- 객체 지향 설계 원칙에 어긋나기 때문에 수정이 어려워지고, 유지보수의 비용이 높아질 수 있습니다.
- 멀티쓰레드 환경에서 동기화 처리를 안하면 인스턴스가 2개가 생성 될 수 있는 가능성이 생기게 됩니다.
이러한 이유로 싱글턴 패턴은 꼭 필요한 경우가 아니라면 지양해야 됩니다.
예시
'얄팍한 코딩사전 : 객체지향 디자인패턴 1' 을 참고했습니다.
상황
사용자가 앱을 사용하는데 세팅(Settings)에서 다크모드(darkMode)를 설정해놓으면
다른 페이지(FirstPage -> SecondPage)로 이동하더라도 이 다크모드(darkMode)가 그대로 유지되어있어야 하는 상황입니다.
그러기 위해서는 어떤 페이지(FirstPage나 SecondPage)에 있든 이 세팅(Settings)을 관리하는 객체는 반드시 같은 것을 사용해야 합니다.
Before
public class Settings {
private boolean dartMode = false;
private int fontSize = 13;
public boolean getDartMode() { return dartMode; }
public int getFontSize() { return fontSize; }
public void setDartMode(boolean dartMode) { dartMode = dartMode; }
public void setFontSize(int fontSize) { fontSize = fontSize; }
}
- Settings에서는 다크모드 여부(darkMode)와 폰트 사이즈(fontSize)를 설정하고 접근할 수 있게 되어있습니다. (getter, setter)
public class FirstPage {
private Settings settings = new Settings();
public void setAndPrintSettings() {
settings.setDartMode(true);
settings.setFontSize(15);
System.out.println(settings.getDartMode() + " " + settings.getFontSize());
}
}
- 첫번째 페이지(FirstPage)에서는 darkMode = true, fontSize = 15이 출력됩니다.
public class SecondPage {
private Settings settings = new Settings();
public void printSettings() {
System.out.println(settings.getDartMode() + " " + settings.getFontSize());
}
}
- 두번째 페이지(SecondPage)에서는 darkMode = false, fontSize = 13가 출력됩니다.
public class MyProgram {
public static void main (String[] args) {
new FirstPage().setAndPrintSettings();
new SecondPage().printSettings();
}
}
- FirstPage와 SecondPage에서의 dartMode, fontSize값이 다른 이유는 Settings는 각각 새로 생성되어 넣어진 서로 다른 객체이기 때문입니다. (안드로이드는 페이지마다 class를 만든다는 전제)
After
public class Settings {
private Settings() {};
private static Settings settings = null;
public static Settings getSettings() {
if (settings == null) {
settings = new Settings();
}
return settings;
}
private boolean dartMode = false;
private int fontSize = 13;
public boolean getDartMode() { return dartMode; }
public int getFontSize() { return fontSize; }
public void setDartMode(boolean dartMode) { dartMode = dartMode; }
public void setFontSize(int fontSize) { fontSize = fontSize; }
}
3 : 먼저 생성자를 private으로 만듭니다. -> 그럼 다른 클래스에서 new로 생성하지 못합니다.
4 : 그리고 static으로 클래스 자기 자신인 Settings 타입의 객체를 하나 작성합니다.
- 클래스 안의 static이 아닌 변수나 메소드들은 객체가 생성될 때마다 메모리의 공간을 새로 차지하지만,
static으로 선언된 것들은 객체가 얼마나 만들어지든 메모리의 지정된 공간에 딱 하나씩만 존재하게 됩니다.
public class FirstPage {
private Settings settings = Settings.getSettings();
public void setAndPrintSettings() {
settings.setDartMode(true);
settings.setFontSize(15);
System.out.println(settings.getDartMode() + " " + settings.getFontSize());
}
}
public class SecondPage {
private Settings settings = Settings.getSettings();
public void printSettings() {
System.out.println(settings.getDartMode() + " " + settings.getFontSize());
}
}
- 결과 : FirstPage, SecondPage 의 결과는 true 15로 나오게 됩니다.
의문점
왜 정적변수를들을 사용하지 않고, 싱글턴을 쓸까?
interface의 사용이나 lazy loading 등 싱글턴으로 할 수 있는 것이 더 많기 때문입니다.
⭐️ 참고한 곳
얄팍한 코딩사전 : 객체지향 디자인패턴 1
https://www.youtube.com/watch?v=lJES5TQTTWE&t=1s
https://devmoony.tistory.com/43
배워야 할 것이 더 많은 주니어 개발자입니다. 🐣
내용 전달보다는 정리를 목적으로 포스팅을 하고 있습니다.
잘못 된 내용이나 부족한 부분은 댓글로 주시면 감사드리겠습니다.