반응형

 

싱글톤 패턴은 소프트웨어 디자인 패턴중 하나이다.

 

주로 사용되는 경우는 단 하나만 존재해야되는 물건이 있을 때!

프로그램 내부에서 단 1개만 생성됨을 보장하며 멀티쓰레드에서 이 객체를 동시에 접근하는 경우에 발생하는 동시성 문제를 해결해준다.

 

말로설명하면 어렵고 코드를 보면 쉽다.

class Order {
    public void start() {
        A a = A.getInstance();
        A b = A.getInstance();
        // 확인
        System.out.println(a == b); // true
    }
}

 

만약 이렇게 있다고 치면, a 와 b 는 같은 객체를 보고있게 된다.

이런 로직을 만들 필요가 있을때 싱글톤패턴을 사용한다.

 


1. 기본적인 구현방법 (리소스낭비)

public class A {

	private A() {
           System.out.println("Hello singleton!!");
	}

	private static A singleton = new A(); //객체생성

	public static A getInstance() {
            return A;
	}

}

 

private 생성자를 사용하여 외부에서의 생성을 막은것이 포인트다

단점으로는 클래스가 로드될때 객체가 생성되므로 리소스 낭비가 있을 수 있다.

 

그렇다면 어떻게 개선할수있을까?

 

2. 기본적인 구현방법 (리소스낭비 줄임)

public class A {
    // 단 1개만 존재해야 하므로 static
    private static A a;

    // private로 외부생성을 막음
    private A() {
       System.out.println("Hello singleton!!");
    }

    //외부에서는 getInstance로 접근
    public static A getInstance() {
      if (instance == null){
          instance = new A();
      }
      return instance; 
    } 
}

 

1) getInstance()

 - static을 쓰지않으면 외부에서 사용할때

new A().new getInstance()

이렇게 새로운 인스턴스를 선언하면서 써야하기때문에 싱글톤이 보장되지 않는다.

따라서 static을 사용하고 if 문을 통하여 기존에 불려진 instance가 없다면 생성, 있다면 기존 instance를 return

 

하지만 단점!

이 경우 Multi Thread 환경에서 Thread-safe 를 보장해주지 않는다.

n개의 사용자가 동시에 getInstance를 했을때 new A() 를 n번 생성할수도있다.

 

이를 막기위해서 Synchronized, Double check 등을 사용해서 막아볼수도 있겠지만, 가장많이 쓰이는 방법은 아래와 같다.

 

3. LazyHolder 싱글톤 패턴 ✔️

public class A {

    private A() {
       System.out.println("Hello singleton!!");
    }
    
    private static class LazyHolder() {
        private static final A instance = new A();
    }

    public static A getInstance() {
        return LazyHolder.instance;
    }
    
}

 

 

훨씬 코드가 예뻐진것을 볼수있다.

 

해당 클래스가 로드되더라도 getnstance() 메서드가 호출될때까지 로드되지 않으므로 리소스 낭비도 줄이고

 

A.getInstance() 를 호출할때마다 동일한 instance가 변환된다. 왜냐! private static final 로 선언했기때문!

 

사용은 아래와 같이 쓰면된다

public static void main(String[] args) {
	A a = A.getInstance();
}

Spring 의 경우에는 static 기반의 싱글톤 구현을 지양하고있으며 더욱 간단하게 쓸수있다.

 

Spring - @Autowired 사용

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class A {
    public A() {
        System.out.println("Hello singleton!!");
    }
}

@Component
public class Main {
    @Autowired
    private A a; // A의 싱글톤 인스턴스가 주입됨

    public void display() {
        System.out.println(a);
    }
}

// 메인 메서드
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class App {
    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
        Main main = context.getBean(Main.class);
        main.display();

        // 다시 가져와서 같은 인스턴스인지 확인
        Main anotherMain = context.getBean(Main.class);
        System.out.println(main.a == anotherMain.a); // true, 같은 인스턴스
    }
}

 

위와 같이 인스턴스 필드로 의존성 주입을 사용하는 경우, 싱글톤 패턴이 유지된다.

Spring 컨테이너는 @Component, @Service, @Repository, @Controller 등으로 정의된 클래스의 인스턴스를 싱글톤으로  관리한다

 

항상 어노테이션을 사용하여 써왔기때문에 싱글톤 패턴을 정석으로 봤을때 생소했던것같다.

728x90

'CS' 카테고리의 다른 글

종속성, 의존성  (0) 2024.10.31
반응형

 

항상 사용하고있던 종속성, 의존성에 대한 고찰.

 

종속성

: 한 클래스가 다른 클래스나 기능을 필요로 할때 발생하는 관계.

예를들어 class B 가 class A 의 변수 혹은 함수에 의존한다면, class B는 class A의 종속성을 가진다고 할 수 있다.

 

의존성

: 종속성을 관리하는 방식.

흔히 알고있는 DI (Dependency Injection) 의존성 주입 이라고한다.

 

예를들어 class B에서 class A를 선언하지않고, 외부에서 class A의 인스턴스를 주입받으면 class A의 속성이나 인스턴스 그대로 사용가능하다.

 

이것을 왜 헷깔리고 자주 잊는가?

나는 개인적으로 외부 class를 선언하고 사용하는것에 너무 익숙해져있기 때문이다.

 

그러한 행위가 가능한 이유는 전부 DI 덕분이라는것을 다시 생각해보았다. 

728x90

'CS' 카테고리의 다른 글

싱글톤 패턴의 이해와 응용  (0) 2024.10.31

+ Recent posts