디자인 패턴 : 싱글톤 패턴

timothy jeong·2022년 7월 7일
0

디자인패턴

목록 보기
1/2

선배 개발자에게 더 좋은 프로그램을 만들려면 어떻게 해야하냐고 물었을때, 디자인 패턴을 공부하라고 했다. 그래서 공부를 시작한다.

디자인 패턴 : 프로그램을 설계할 때 발생하는 물제들을 객체간의 상호 관계등을 이용하여 해결할 수 있도록 하는 규약

싱글톤 패턴

싱글톤 패턴이란 하나의 클래스에 오직 하나의 인스턴스만 가지는 패턴이다. 스프링 프레임워크에서 Bean 으로 등록된 클래스들은 모두 싱글턴으로 관리되므로 상태관리에 신경써야한다는 이야기를 들었다. 그때는 그냥 그렇구나 하고 넘어갔는데, 이제는 왜 쓰는지 알아보자.

장점

싱글톤 패턴이 갖는 장점은 하나의 인스턴스만을 모든 모듈에서 공유하기 때문에 인스턴스 생성시 발생하는 비용이 줄어든다는 장점이 있다.

대표적으로 인스턴스 생성 비용이 큰 것은 DB와의 커넥션 연결이다.
spring 프레임워크를 예로 들자면, JPA의 EntityManagerFactory 를 생성하는 경우일 것이다.

단점

싱글톤 패턴은 단위 테스트 진행시 걸림돌이 된다.
단위 테스트는 테스트가 상호간 독립적으로 시행되어야 하며, 어떤 순서로든 테스트를 시행할 수 있어야 하는데, 테스트 과정에서 싱글톤으로 구현된 인스턴스를 사용해야 한다면 각 테스트를 독립적으로 만들기 어려워진다.

의존성 주입 장점

이러한 싱글턴 패턴의 단점은 모듈간의 의존성을 높이는 것이라고 할 수 있을 것이다. 이러한 객체간 결합을 완화해주는 것이 의존성 주입이다.

싱글턴 모듈이 직접 다른 모듈에서 활용되는 것을 개선하여, 다른 모듈에서 사용되는 싱글톤 모듈을 의존성 주입자에서 관리하고, 이 싱글톤 모듈을 활용하는 다른 모듈은 오로지 의존성 주입자를 통해서 싱글톤 모듈을 주입받게 된다.

이렇게 하면 싱글톤 모듈과 다른 모듈 사이에 의존성 주입자를 넣게되면, 주입되는 모듈을 쉽게 교체할 수 있는 구조가 되어 테스트가 용이해지고, 추상화된 레이어를 주입하도록 설계하고 실제로는 이를 구현한 구현체를 주입하도록 하여 변화에 유연한 프로그램을 설계할 수 있으며, 의존성 방향이 일관되고, 추론이 쉬워지며 모듈간 관계가 명확해진다.

SOLID 원칙이 잘 적용된 사례가 의존성 주입이라고 할 수 있겠다.

의존성 주입 단점

모듈이 더욱더 분리되므로 클래스 수가 늘어나 복잡성이 증가될 수 있으며 약간의 런타임 패널티가 발생할 수 있다.

의존성 주입의 원칙

(1) 상위 모듈(예시에서 싱글톤 모듈)은 하위 모듈에서 어떠한 것도 가져오지 말아야한다.
(2) 둘다 추상화에 의존해야 하며, 이때 추상화는 세부 사항에 의존하지 말아야 한다.

구현

내가 자주 사용하는 프로그래밍 언어에서 싱글톤 패턴을 구현해보자.

java

class Singleton {
	private static class singletonInstance {
    	private static final Singleton INSTANCE = new Singleton();
    }
    public static synchronized Singleton getInstance() {
    	return singletonInstance.INSTANCE;
    }
}
  • synchronized : 자바에서 여러 쓰레드가 동일한 자원에 대한 접근을 하려고 할때 현재 데이터를 사용하고 있는 해당 스레드를 제외하고 나머지 스레드들은 데이터에 접근 할 수 없도록 하는 키워드이다.
    싱글톤 객체는 여러 하위 모듈에서 사용하는 것을 가정하고 있으며, 싱글톤객체가 state 를 가지고 있다면, 여러 쓰레드가 동시에 사용하는 것은 문제를 일으킬 수 있으므로 synchronized 키워드를 사용해야 한다.

kotlin

// 생성시 주입받을 파라미터가 없다면
object Singleton{ }

// 생성시 주입받을 파라미터가 있다면
class Singleton private constructor() {

    companion object {
        private var instance: SingletonWithParam? = null

        private lateinit var context: Context

        fun getInstance(_context: Context): SingletonWithParam {
            return instance ?: synchronized(this) {
                instance ?: SingletonWithParam().also {
                    context = _context
                    instance = it
                }
            }
        }
    }
}

object 키워드만 사용해서 클래스를 만드는것은 생성자를 통해 파라미터를 받지 않는 경우에는 가능하다.

하지만 생성자를 통해 파라미터를 주입받는 경우에는 companion object 를 통해 내부에 클래스를 따로 정의해줘야 하는데, kotlin 은 안드로이드 개발시 사용되는 경우가 많고, 안드로이드에서는 클래스 내부에서 Context 를 사용하는 경우가 많기 때문에 이러한 방법이 자주 사용될 수 있다.

javascript

class Singleton {
  constructor() {
    if (!Singleton.instance) {
      Singleton.instance = this
    }
    return Singleton.instance
  }
  getInstance() {
    return this.instance
  }
}
profile
개발자

0개의 댓글