OOP와 생산성 – 다형성의 문제점? 을 읽다보니 다음과 같이 얘기가 나온다.

OOP 덕에 전역 변수가 많이 줄었다는 것은 아쉽게도 착각입니다. 일례로 Singleton Pattern을 보세요. 이건 사용이 장려되고 있는 (디자인 패턴이라는 탈을 쓰고 나타난) 전역 변수 아닌가요?

싱글톤 패턴이 전역변수인가?

구글에서 애자일 코치로 활동하면서 테스트편의성을 가진 깔끔한 코드를 작성하도록 개발자들을 돕고 있는Misko Hevery에 따르면 그렇다. 그는 "싱글톤패턴은 양의 탈을 쓴 전역상태(global state)"라고 실날하게 비판한다. 싱글톤패턴은 전역상태를 조장하고 그 때문에 설계상의 문제점을 찾기 힘들게 하며 테스트하기 어렵게 만드는 안티패턴이라는 것이다.

그는 심지어 싱글톤을 적용한 부분을 찾아서 제거하는데 도움이 되도록, 클래스 파일의 바이트코드를 분석해서 싱글톤패턴을 찾아주는 Google Singleton Detector를 개발하기도 했다. 이 싱글톤 디텍터는 전형적인 싱글콘코드 뿐 아니라 변조 싱글톤인 Fingleton, Mingleton, Hingleton도 찾아준다.

싱글톤과 전역상태의 문제점에 관해서는 그의 Clean Code Talk 시리즈의 하나인 Global State and Singleton을 보면 이해하는데 도움이 될 것이다.

 

Misko Hevery 말고도 싱글톤패턴에 대해서 매우 비판적인 사람을 꼽자면 바로 스프링을 만든 로드 존슨이다. 로드 존슨은 자바의 싱글톤패턴의 단점을 극복하는 방법의 하나로 스프링 컨테이너와 같은 싱글톤 레지스트리가 필요하다고 주장한다. 테스트를 어렵게 만들고, 글로벌하게 접근하기 쉽게 만드는 싱글톤패턴 대신 public 생성자를 가진 평범한 POJO를 만들어 사용하되, 상태없는 서비스 싱글톤으로 사용이 필요하면 컨테이너를 통해서 싱글톤으로 관리되도록 만들면 된다는 것이다. IoC싱글톤이라고 해야 하나. 아무튼 스프링의 핵심 컨테이너의 빈 관리를 담당하는 BeanFactory의 핵심 구현 클래스는 DefaultListableBeanFactory이다. 대부분의 애플리케이션 컨텍스트는 바로 이 클래스를 BeanFactory로 사용하는데, 이 DefaultListableBeanFactory가 구현하고 있는 인터페이스의 한가지가 바로 SingletonRegistry이다. 결국 스프링 컨테이너 = 애플리케이션 컨텍스트 = 빈 팩토리 = 싱글톤 레지스트리가 되는 것이다.

나는 스프링을 사용한 이후로 싱글톤 패턴을 한번도 적용해본 적이 없다. 스프링을 사용하면 싱글톤의 장점은 얻으면서도 편리한 테스트를 만들 수 있고 전역접근에 대한 위험은 피하는 클래스의 작성이 쉽게 가능하기 때문이다.

 

DL(lookup)이 DI(injection)보다 못한점을 꼽자면 보통 lookup API의 노출과 컨테이너/컨텍스트를 거치지 않고 테스트 등에서 직접 POJO를 다루고 수동 DI하기가 힘들다는 점을 들 수 있다. 거기다 하나 더 추가하자면 DL은 전역상태를 사용하는 코드를 만들기 쉽다는 점을 들 수 있을 것 같다. 스프링의 모든 빈은 이너빈이 아니라면 전역적으로 접근이 가능하기 때문이다. 하지만 DI는 컨테이너를 코드에서 완벽하게 감추기 때문에 미리 설정된 구조를 따르지 않고는 다른 빈에 접근하지 않도록 해준다. 그에 반해서 DL은 언제든지 빈 이름을 통해서 임의의 빈을 액세스 할 수 있는 위험한 기능에 노출되어있다. 물론 조심해서 사용하면 상관없지만 잘못 사용하면 생성방식만 다를 뿐 싱글톤 패턴의 문제점을 가질 수 밖에 없다

 

어제 KSUG에 간단한 퀴즈를 냈는데 쉬운 질문이지만 트릭이 있는 것이었다. 싱글톤패턴으로 만든 클래스처럼 private 생성자를 가져서 외부에서 new로 생성이 불가능한 클래스를 스프링의 빈으로 바로 등록해서 사용할 수 있는가하는 질문이었다. 상식적으로 생각하자면 private생성자를 가졌으니 안될 것이고, 대신 getInstance() 같은 스태틱 팩토리를 가지고 있으니 스프링에서 스태틱 팩토리 메소드를 지정해서 팩토리 빈으로 만들서 사용하면 된다고 볼 수 있다. 그런데 정답은 "private 생성자를 가진 클래스도 스프링의 빈으로 바로 사용이 가능하다"이다. 스프링은 리플렉션을 통해서 인스턴스를 만들고, 리플렉션을 통해서라면 private생성자를 호출해서 인스턴스를 만드는 것이 가능하다. 해보면 알겠지만 스프링은 별 경고 없이 그냥 빈을 만들어버린다.

그래서 기존에 싱글톤패턴으로 만든 클래스나 팩토리 메소드를 통해서만 오브젝트가 만들어지는 클래스를 그냥 스프링의 빈으로 등록해서 DI방식으로 사용하게 하면 싱글톤패턴 코드로서의 단점을 극복하고, 테스트 편의성을 가져올 수도 있다. 굳이 오브젝트 팩토리 방식을 쓰거나 팩토리 빈으로 지정하지 않아도 된다는 것이다.

하지만 그다지 권장하고 싶지는 않다. 가능하면 클래스 설계자의 의도를 존중하고, 접근방법을 지키도록 하는 것이 바람직하다. 팩토리 메소드를 적용한 특별한 의도가 있거나 인스턴스 생성시 부가 작업이 필요한 경우가 있기 때문에 private생성자를 두었을 수도 있는데, 이를 무시하고 리플렉션을 통해서 가능하다고 직접 생성자를 호출해서 만드는 것은 다른 문제를 일으킬 수 있기 때문이다.

 

결론은 스프링을 쓰자.


출처 - http://toby.epril.com/?p=849


===================================================================================


Application Registry를 이용하여 Singleton의 확산을 피하라

Singleton을 사용할 때의 문제점(187페이지) => static을 사용할 때의 문제점이 될 수 있다.

  • Singleton 클래스에 대한 의존관계는 다른 클래스에 하드코딩되어야 한다.
  • Singleton은 그 자신의 설정을 처리해야 한다. => 이해가 잘 되지 않는다.
  • 복잡한 애플리케이션은 많은 Singleton을 가진다. 각 클래스는 서로 다른 설정 로딩을 갖는다. 이는 설정을 위한 중앙 Storage가 없음을 의미한다.
  • Singleton은 인터페이스와 친밀하지 않다. Singleton의 일반적인 구현은 Interface가 아닌 클래스에 타입을 정의하는 것이다.
  • Singleton은 상속을 잘 따르지 않는다. 자바는 static method의 오버라이딩을 허용하지 않기 때문이다.
  • Singleton의 상태를 Runtime시 일관성 있게 갱신하는 것이 불가능하다.

Rod Johnson은 일반적으로 static 변수를 사용하는 것을 좋아하지 않는다. 이는 특정 클래스에 대한 의존성을 만듦으로써 객체 지향을 깨트린다.

ApplicationContext의 등장

  • Rod Johnson은 Singleton 패턴의 이 같은 단점을 보완하기 위하여 ApplicationContext라는 Registry를 만들었다.

Global State와 Singleton 패턴

Singleton 패턴

  • http://www.ibm.com/developerworks/kr/library/j-dcl.html : Singleton 패턴을 사용할 때 발생하는 Double-checked locking 문제에 대해서 설명하고 있으며, 해결 방법을 제시해주고 있다.
    • ApplicationContext 개념을 사용할 경우 Double-checked locking에 대해서 고민하지 않아도 된다


출처 - http://www.javajigi.net/pages/viewpage.action?pageId=207388870






Posted by linuxism
,