본문 바로가기

개발공부/디자인패턴

[디자인패턴] 싱글턴 패턴(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

 

 

배워야 할 것이 더 많은 주니어 개발자입니다. 🐣
내용 전달보다는 정리를 목적으로 포스팅을 하고 있습니다.
잘못 된 내용이나 부족한 부분은 댓글로 주시면 감사드리겠습니다. 
반응형