싱글 톤이 왜 그렇게 나쁜가요? [닫은]


1982

싱글 톤 패턴 의 완전 최대 유료 회원입니다 GoF의패턴 책 , 그러나 그것은 최근에 오히려 개발자 세계 고아 보인다. 나는 여전히 많은 팩토리 클래스를 사용합니다. 특히 팩토리 클래스 에는 멀티 스레딩 문제에 대해 조심해야하지만 (실제로 다른 클래스와 마찬가지로) 왜 그렇게 끔찍한 지 알 수 없습니다.

스택 오버플로는 특히 모든 사람들이 싱글 톤이 악하다고 동의한다고 가정합니다. 왜?

" 사실, 참조 또는 특정 전문 지식 "으로 답변을 지원하십시오.


6
코드를 조정하려고 시도하면서 싱글 톤 디자인을 사용하면 최근에 화상을 입었다 고 말해야합니다. 자유 시간에 할 때, 나는 이것을 리팩토링하기에는 거의 게으르다. 생산성에 대한 나쁜 소식.
Marcin

71
답변에는 많은 '죄'가 있지만, 패턴이 좋을 때와 나쁜 것과 대조되는 좋은 예를보고 싶습니다.
DGM

50
: 나는 몇 달 전에 주제에 대한 블로그 포스트를 작성 jalf.dk/blog/2010/03/...을 - 나를 그냥 크게 그것을 말할 수 있습니다. 나는 싱글 톤이 올바른 솔루션이라는 단일 상황을 개인적으로 생각할 수 없습니다. 그렇다고 그런 상황이 존재하지 않는다는 것을 의미하는 것은 아닙니다.
jalf

8
그것이 의미하지 않는다 @AdamSmith 당신은 에, 그러나 당신이 의미 할 수 그런 식으로 액세스 할 수 있습니다. 그런 식으로 액세스하지 않으려면 처음에 싱글 톤으로 만들어야 할 이유가 거의 없습니다. 따라서 당신의 주장은 "단일 톤으로 취급 하지 않으면 싱글 톤을 만드는 데 아무런 해가 없습니다 . 예, 좋습니다. 내가 운전하지 않으면 내 차도 오염되지 않습니다. ;) (전체 공개 : 나는 실제로 자동차가 없습니다)
jalf

35
이 전체 주제의 최악의 부분은 싱글 톤을 싫어하는 사람들이 대신 무엇을 사용해야하는지에 대한 구체적인 제안을 거의하지 않는다는 것입니다. 예를 들어,이 SO 기사를 통해 저널 기사 및 자체 공개 블로그에 대한 링크 는 싱글 톤을 사용 하지 않는 이유 (그리고 모두 훌륭한 이유 임)에 대해서는 계속하지만 대체가 매우 적습니다. 그러나 많은 양의 손짓. 새로운 프로그래머에게 싱글 톤을 사용하지 않는 이유를 가르치려고하는 사람들은 좋은 타사의 예제가 많지 않으며, 단지 예를 들어 설명 할뿐입니다. 피곤해
Ti Strga

답변:


1293

브라이언 버튼에서 번역 :

  1. 그것들은 일반적으로 글로벌 인스턴스로 사용되는데 왜 그렇게 나쁜가? 인터페이스를 통해 노출하는 대신 코드에서 애플리케이션의 종속성을 숨기므로 그것을 피하기 위해 전역으로 무언가를 만드는 것은 코드 냄새 입니다.

  2. 그들은 하나의 책임 원칙을 위반합니다 . 즉, 자신의 생성과 수명주기를 제어한다는 사실 때문입니다.

  3. 이것들은 본질적으로 코드가 밀접하게 결합되도록 합니다. 이로 인해 많은 경우 테스트중인 가짜가 다소 어려워집니다.

  4. 응용 프로그램 수명 동안 상태를 유지합니다. 테스트를 주문 해야하는 상황으로 이어질 수 있으므로 테스트에 대한 또 다른 타격은 단위 테스트에 큰 도움이되지 않습니다. 왜? 각 단위 테스트는 서로 독립적이어야합니다.


324
난 너의 의견에 반대해. 의견은 600 자만 허용되므로 이에 대한 의견을 남기기 위해 블로그 게시물을 작성했습니다. 아래 링크를 참조하십시오. jorudolph.wordpress.com/2009/11/22/singleton-considerations
요하네스 루돌프

61
포인트 1과 4에서 싱글 톤이 유용하며 실제로 데이터 캐싱 (특히 DB)에 거의 완벽하다고 생각합니다. 성능 향상은 모델링 단위 테스트와 관련된 복잡성을 훨씬 능가합니다.
Dai Bok

56
@Dai Bok : "데이터 캐싱 (특히 DB에서)"
은이

55
와우, 좋은 반응. 나는 아마도 여기에 지나치게 공격적인 표현을 사용 했으므로, 부정적으로 고안된 질문에 대답하고 있음을 명심하십시오. 내 대답은 잘못된 싱글 톤 사용으로 인해 발생하는 '안티 패턴'에 대한 짧은 목록입니다. 완전한 공개; 나도 가끔 싱글 톤을 사용합니다. SO에 대해 더 중립적으로 제기 된 질문이있어 싱글 톤이 좋은 아이디어로 간주 될 수있는시기에 대한 훌륭한 포럼이 될 것입니다. 예를 들어 stackoverflow.com/questions/228164/…
Jim Burger

21
다중 스레드 환경에서 캐싱하기에는 그리 좋지 않습니다. 제한된 리소스를 놓고 싸우는 여러 스레드로 캐시를 쉽게 물리 칠 수 있습니다. [싱글 톤에 의해 유지]
monksy

449

싱글 톤은 하나의 문제 만 해결합니다.

리소스 경합.

자원이 있다면

( 1 )은 단일 인스턴스 만 가질 수 있으며

( 2 ) 단일 인스턴스를 관리해야합니다.

싱글 톤 이 필요합니다 .

예가 많지 않습니다. 로그 파일이 큰 파일입니다. 단일 로그 파일 만 포기하고 싶지는 않습니다. 플러시, 동기화 및 올바르게 닫으려고합니다. 이것은 관리해야하는 단일 공유 리소스의 예입니다.

싱글 톤이 필요한 경우는 거의 없습니다. 그들이 나쁜 이유는 그들이 글로벌 한 느낌과 GoF Design Patterns 책 의 전액을 지불했기 때문 입니다.

글로벌이 필요하다고 생각하면 아마도 끔찍한 디자인 실수를 저지른 것일 수 있습니다.


43
하드웨어도 좋은 예입니까? 임베디드 시스템에는 싱글 톤을 사용하는 많은 하드웨어가 있습니까? <grin>
Jeff

170
전적으로 동의합니다. 연습이 그 자리를 차지할 수 있다는 인식이없이 떠 다니는 "이 연습이 나쁘다"라는 말이 많이 있습니다. 종종, 종종 잘못 사용되기 때문에 연습은 "나쁜"것입니다. 적절하게 적용되는 한 Singleton 패턴에는 본질적으로 아무런 문제가 없습니다.
Damovisa

53
원칙적으로 싱글 톤은 매우 드물다 (그리고 프린터 큐는 확실히 하나도 아니며 로그 파일도 아니다-log4j 참조). 종종 하드웨어는 원칙이 아니라 우연의 일치입니다. PC에 연결된 모든 하드웨어는 단일 우연의 일치입니다 (여러 모니터, 마우스, 프린터, 사운드 카드를 생각하십시오). 500 마이크로 미터의 입자 탐지기조차도 우연의 일치와 예산 제약으로 소프트웨어에는 적용되지 않으므로 싱글 톤은 없습니다. 통신에서 전화 번호 나 실제 전화는 싱글 톤이 아닙니다 (ISDN, 콜 센터 생각).
digitalarbeiter

23
하드웨어에는 다양한 리소스의 인스턴스가 하나만있을 수 있지만 하드웨어는 소프트웨어가 아닙니다. 개발 보드의 단일 직렬 포트를 싱글 톤으로 모델링 할 이유가 없습니다. 실제로이를 모델링하면 두 개의 포트가있는 새 보드로 이식하기가 더 어려워집니다!
dash-tom-bang

19
따라서 두 개의 동일한 클래스를 작성하여 많은 코드를 복제하면 두 개의 포트를 나타내는 두 개의 싱글 톤을 가질 수 있습니까? 두 개의 전역 SerialPort 객체를 사용하는 것은 어떻습니까? 하드웨어를 클래스로 모델링하지 않는 것은 어떻습니까? 그리고 왜 단일 액세스를 사용합니까?이 또한 전역 액세스를 의미합니까? 당신이 원하는 마십시오 모든 직렬 포트에 액세스 할 수 있도록 코드에 기능을?
jalf

352

일부 코딩 스 너브 (snob)는 그것들을 단지 영광스러운 세계로 봅니다. 많은 사람들이 goto 선언을 싫어하는 것과 같은 방식 으로 글로벌 사용을 싫어하는 사람들이 있습니다 . 나는 여러 개발자 가 실패를 받아들이는 것을 고려했기 때문에 전 세계 를 피하기 위해 엄청난 길이로 진행하는 것을 보았습니다 . 이상하지만 사실입니다.

실제로 Singleton 패턴은 개념 툴킷의 유용한 부분 인 프로그래밍 기술 일뿐입니다. 때때로 당신은 그것이 이상적인 솔루션임을 알 수 있으므로 그것을 사용하십시오. 그러나 디자인 패턴을 사용하는 것에 대해 자랑 할 수 있도록 그것을 사용하는 것은 단지 글로벌 이기 때문에 디자인 패턴을 사용하는 것을 거부하는 것처럼 바보 입니다.


17
Singleton에서 볼 수없는 실패는 사람들이 어떻게 든 "더 나은"이유로 글로벌 대신 글로벌을 사용한다는 것입니다. 문제는 (내가 알듯이) 싱글 톤이 테이블에 가져 오는 것들과 관련이 없다는 것입니다. (예를 들어, 실제로 생성자를 사용하지 않는 경우에도, 처음 사용시의 구성은 단일이 아닌 환경에서 구현하는 것이 쉽지 않습니다.)
dash-tom-bang

21
"우리"가 그것들을 내려다 보는 이유는 "우리"가 잘못 사용하는 것을 매우 자주보고 너무 자주 사용하기 때문입니다. "우리"는 그들이 원인이 있다는 것을 알고 있습니다.
Bjarke Freund-Hansen

5
@Phil, 당신은 "때때로 그것이 이상적인 솔루션이라는 것을 알았을 것입니다"라고 말했습니다. 그렇다면 어떤 경우에 싱글 톤이 유용할까요?
Pacerier

22
@Pacerier, 다음 조건이 모두 충족 될 때마다 : (1) 무언가 중 하나만 필요합니다. (2) 많은 수의 메소드 호출에서 인수로 한 가지를 전달해야합니다. (3) 기꺼이 어디에서나 대담한 것을 전달하지 않음으로써 코드의 크기와 복잡성을 즉시 줄이려면 언젠가 리팩터링해야 할 기회.
antinome

18
나는 이것이 아무것도 대답하지 않는다고 느끼지 않으며 단지 '때로는 맞을 수도 있고 그렇지 않을 수도 있습니다'라고 말합니다. 하지만 왜, 언제? 이 답변이 중재에 대한 논쟁 이상의 이유는 무엇입니까 ?
Guildenstern

219

구글의 Misko Hevery는이 주제에 관한 흥미로운 기사를 가지고 있습니다.

싱글턴은 병리학적인 거짓말 쟁이 거짓말 쟁이싱글 톤이 의존성 체인을 파악하고 응용 프로그램을 시작하거나 테스트하는 것을 어렵게 만드는 방법을 보여주는 단위 테스트 예제를 가지고 있습니다. 그것은 학대에 대한 극단적 인 예이지만, 그가 주장하는 요점은 여전히 ​​유효합니다.

싱글 톤은 글로벌 상태에 지나지 않습니다. 전역 상태를 사용하면 객체가 API에 선언되지 않은 것을 비밀리에 보유 할 수 있으므로 싱글 톤은 API를 병리학적인 거짓말 쟁이로 만듭니다.

모든 Singletons Gone 이 의존성 주입을 통해 인스턴스를 필요로하는 생성자에게 인스턴스를 쉽게 가져올 수있게되었으므로 첫 번째 기사에서 언급 한 나쁜 글로벌 Singleton의 근본적인 요구를 완화 할 수있었습니다.


8
주제에 관한 Misko의 기사는 현재 가장 좋은 방법입니다.
Chris Mayer

22
첫 번째 링크는 실제로 싱글 톤 문제를 해결하는 것이 아니라 클래스 내부의 정적 종속성을 가정합니다. 매개 변수를 전달하여 주어진 예제를 수정하면서도 여전히 싱글 톤을 사용할 수 있습니다.
DGM

17
@DGM : 정확히-실제로 기사의 '이론적'부분과 '싱글 론이 원인'부분 사이에 논리적으로 큰 차이가 있습니다.
Harper Shelby

1
Misko의 CreditCard 기사는 이 패턴을 잘못 사용 하는 극단적 인 예입니다.
Alex

2
두 번째 기사 읽기 싱글 톤은 실제로 설명 된 개체 모델의 일부입니다. 그러나 전역 적으로 액세스 할 수있는 대신 팩토리 객체에 대해 비공개입니다.
과일 브레이크

121

혼동은 사람들이 싱글 톤 패턴의 실제 적용을 알지 못하기 때문에 발생한다고 생각합니다. 나는 이것을 충분히 강조 할 수 없다. 싱글 톤은 글로벌을 감싸는 패턴 이 아닙니다 . 단일 클래스 패턴은 런타임 동안 주어진 클래스의 인스턴스가 하나만 존재 하도록 보장하기 위해 사용해야합니다 .

사람들은 싱글 톤을 글로벌에 사용하기 때문에 싱글 톤이 악하다고 생각합니다. 이 혼동으로 인해 싱글 톤을 내려다 보았습니다. 싱글 톤과 글로벌을 혼동하지 마십시오. 원래 목적에 맞게 사용하면 싱글 톤 패턴의 이점을 최대한 활용할 수 있습니다.


20
앱 전체에서 사용할 수있는 하나의 인스턴스는 무엇입니까? 오. 글로벌 . "Singleton은 전역을 감싸는 패턴 이 아닙니다 "는 정의 에 따라 패턴이 전역 인스턴스를 둘러싼 클래스를 감싼 것처럼 순진하거나 오도 합니다.
cHao

26
물론 응용 프로그램이 시작될 때 클래스의 인스턴스 하나를 만들고 인터페이스를 통해 해당 인스턴스를 사용하는 모든 인스턴스에 해당 인스턴스를 주입 할 수 있습니다. 구현은 하나만있을 수 있다는 것을 신경 쓰지 않아야합니다.
Scott Whitlock

4
@Dainius : 결국에는 실제로 없습니다. 물론 인스턴스를 다른 인스턴스로 임의로 바꿀 필요는 없습니다. ( 당신이 있는 얼굴을 유발하는 순간을 제외하고 는 실제로 setInstance이전에 방법을 보았습니다 .) 그다지 중요하지는 않습니다. 단일 톤을 "필요한"것은 캡슐화에 대한 괴물이나 나쁜 점을 알지 못했습니다. 변경 가능한 전역 상태이므로 도움이되는 (?) 모든 사람에게 세터를 제공했습니다. 단일. 들. (그렇습니다. 이런 일이 일어납니다. 많이 . 야생에서 본 거의 모든 싱글 톤은 디자인에 의해 변경 될 수 있었고 종종 당황
스럽습니다

4
@Dainius : 우리는 이미 이미 가지고 있습니다. "상속보다 컴포지션 선호"는 꽤 오랫동안 문제였습니다. 상속 명백히 가장 좋은 해결책 일 때는 물론 자유롭게 사용할 수 있습니다. 싱글 톤, 글로벌, 스레딩 goto등도 마찬가지 입니다. 많은 경우에 효과 가 있을 수 있지만 솔직히 "작동"으로는 충분하지 않습니다. 기존의 지혜에 반하고 싶다면 접근 방식을 더 잘 설명 할 수있었습니다. 이다 더 나은 기존의 솔루션보다. 그리고 나는 아직 Singleton 패턴과 같은 경우를 보지 못했습니다.
cHao

3
우리가 서로 과거에 이야기하지 않도록, 전 세계적으로 사용 가능한 인스턴스에 대해서만 말하는 것이 아닙니다. 그것에 대한 많은 경우가 있습니다. 내가 말하고있는 것은 (특히 "capital-S Singleton"이라고 말할 때) GoF의 Singleton 패턴입니다.이 단일 글로벌 인스턴스를 클래스 자체에 포함시키고 getInstance비슷한 이름의 메소드 를 통해 노출시키고 a 의 존재를 막습니다. 두 번째 사례. 솔직히, 그 시점에서, 당신은뿐만 아니라 심지어이 없을 수 있습니다 하나 개의 인스턴스.
cHao

72

싱글 톤에 대한 한 가지 나쁜 점은 싱글 톤을 매우 쉽게 확장 할 수 없다는 것입니다. 기본적으로 어떤 종류의 데코레이터 패턴 이나 그 동작을 변경하려면 그러한 것을 만들어야합니다. 또한 언젠가 여러 가지 방법으로 하나의 작업을 수행하려는 경우 코드 레이아웃에 따라 변경하기가 다소 어려울 수 있습니다.

한 가지 주목할 점은 싱글 톤을 사용하는 경우 직접 액세스하지 않고 필요한 사람에게 전달하려고 시도하는 것입니다. 그렇지 않으면 싱글 톤이 수행하는 작업을 여러 방법으로 수행하기로 선택한 경우에는 다음과 같이됩니다. 각 클래스가 싱글 톤에 직접 액세스하는 경우 종속성을 포함하므로 변경하기가 다소 어렵습니다.

그래서 기본적으로:

public MyConstructor(Singleton singleton) {
    this.singleton = singleton;
}

오히려

public MyConstructor() {
    this.singleton = Singleton.getInstance();
}

이런 종류의 패턴을 의존성 주입 이라고합니다. 하며 일반적으로 좋은 것으로 간주됩니다.

그러나 어떤 패턴과 마찬가지로 ... 그것에 대해 생각하고 주어진 상황에서의 사용이 부적절한 지 아닌지 고려하십시오 ... 규칙은 일반적으로 깨지도록 만들어지며, 생각없이 패턴 을 적용 할 수 없습니다.


17
Heh, 당신이 어디서나 이것을하면, 당신은 또한 어디에서나 singelton에 대한 참조를 전달해야하므로 더 이상 싱글 톤이 없습니다. : (IMO 일반적으로 좋은 일이된다.)
Bjarke 프로 인트 - 한센

2
@ BjarkeFreund-Hansen-넌센스, 무슨 소리 야? 싱글 톤은 단순히 한 번만 설명되는 클래스의 인스턴스입니다. 이러한 싱글 톤을 참조하면 실제 객체가 복사되지 않고 참조 만됩니다. 여전히 동일한 객체가 있습니다 (싱글 톤 읽기).
M. Mimpen

2
@ M.Mimpen : 아니요. 여기서 설명하는 Capital-S Singleton은 (a) 하나의 인스턴스 만 존재 함을 보장 하고 (b) 클래스 자체를 통해 액세스하는 클래스의 인스턴스입니다. 내장 된 글로벌 액세스 지점. 아무것도 호출하지 않아야한다고 선언 getInstance()한 경우 (b)는 더 이상 사실이 아닙니다.
cHao

2
@cHao 나는 당신을 따르지 않거나 당신이 내가 말하는 사람을 이해하지 못합니다-그것은 Bjarke Freund-Hansen입니다. Bjarke는 싱글 톤에 대한 여러 참조가 있으면 싱글 톤이 여러 개 있다고 말합니다. 딥 카피가 없으므로 확실히 사실이 아닙니다.
M. Mimpen

6
@ M.Mimpen : 나는 의미 론적 효과를 더 언급하기 위해 그의 의견을 취합니다. 에 전화를 걸면 getInstance()싱글 톤 패턴과 일반적인 참조의 유용한 차이점을 효과적으로 찾아 냈습니다. 나머지 코드에 관한 한, 단일성은 더 이상 속성이 아닙니다. 호출자 만이 getInstance()인스턴스가 몇 개인 지 알고 있거나 심지어 신경 써야합니다. 한 명의 발신자 만 있으면 클래스가 단일 참조를 저장하고 재사용하는 것보다 단일성을 안정적으로 적용하는 데 더 많은 노력과 유연성이 필요합니다.
cHao

69

싱글 톤 패턴 자체는 문제가되지 않습니다. 문제는 패턴이 OO 개념을 제대로 파악하지 않고도 객체 지향 도구로 소프트웨어를 개발하는 사람들이 종종 사용한다는 것입니다. 이 문맥에서 싱글 톤이 소개 될 때, 그들은 거의 사용하지 않는 헬퍼 메소드를 포함하는 관리 할 수없는 클래스로 성장하는 경향이 있습니다.

싱글 톤은 테스트 관점에서도 문제가됩니다. 그들은 고립 된 단위 테스트를 작성하기 어렵게 만드는 경향이 있습니다. 제어 역전 (IoC) 및 의존성 주입 은 단위 테스트에 적합한 객체 지향 방식으로이 문제를 극복하기위한 패턴입니다.

A의 쓰레기 수집 환경 싱글 빠르게 메모리 관리와 관련하여 문제가 될 수 있습니다.

싱글 톤이 병목 현상이 될 수있는 멀티 스레드 시나리오와 동기화 문제도 있습니다.


4
나는 그것이 많은 오래된 실이라는 것을 안다. 안녕 @ 키 모즈 말했다 :-싱글 톤은 메모리 관리와 관련하여 신속하게 문제가 될 수 있습니다. 싱글 톤 및 가비지 수집과 관련하여 어떤 종류의 문제가 발생하는지 자세히 설명하고 싶습니다.
Thomas

@Kimoz, 질문은 "단일 패턴이 왜 문제가되지 않는가?"입니다. 포인트를 반복했지만 싱글 톤 패턴의 유효한 단일 사용 사례조차 제공하지 않았습니다.
Pacerier

@Thomas는 정의에 따라 싱글 톤이 하나의 인스턴스에만 존재하기 때문입니다. 따라서 null에 유일한 참조를 할당하는 것은 종종 복잡합니다. 할 수는 있지만 앱에서 싱글 톤을 사용하지 않은 시점을 완전히 제어한다는 의미입니다. 그리고 이것은 드문 일이며 일반적으로 싱글 톤 애호가가 찾는 것과는 정반대입니다 . 단일 인스턴스를 항상 액세스 할 수 있는 간단한 방법 입니다. Guice 또는 Dagger와 같은 일부 DI 프레임 워크에서는 싱글 톤을 제거 할 수 없으며 영원히 기억에 남아 있습니다. (단일 컨테이너가 집에서 만든 컨테이너보다 훨씬 낫다면 컨테이너).
Snicolas

54

싱글 톤은 정적 메소드를 사용하여 구현됩니다. 정적 방법은 조롱하거나 스터 빙 할 수 없으므로 단위 테스트를 수행하는 사람들이 피합니다. 이 사이트의 대부분의 사람들은 단위 테스트의 큰 지지자입니다. 이를 피하기 위해 일반적으로 가장 많이 사용되는 규칙은 반전 제어 패턴을 사용하는 것입니다 .


9
그것은 객체 (단위), 함수 (단위), 전체 라이브러리 (단위)를 테스트 할 수 있지만 클래스 (정적 단위)에서 정적 인 것으로 실패하는 단위 테스트의 문제와 비슷합니다.
v010dya 2019

2
어쨌든 모든 외부 참조를 다루지 않겠습니까? 그렇다면 싱글 톤을 moc하는 데 문제가 무엇입니까? 그렇지 않으면 실제로 단위 테스트를하고 있습니까?
Dainius

@Dainius : 모의 인스턴스 는 모의 클래스보다 문제가 훨씬 적습니다. 응용 프로그램의 나머지 부분에서 테스트중인 클래스를 추출하고 가짜 Singleton클래스로 테스트 할 수 있습니다. 그러나 이는 테스트 과정을 엄청나게 복잡하게 만듭니다. 우선, 마음대로 클래스를 언로드하거나 (대부분의 언어에서 실제로는 옵션이 아님) 각 테스트마다 새 VM을 시작할 수 있어야합니다 (읽기 : 테스트는 수천 시간이 걸릴 수 있음). 그러나 두 가지에 대한 의존성 Singleton은 이제 모든 테스트에서 유출되는 구현 세부 사항입니다.
cHao

Powermock은 정적을 조롱 할 수 있습니다.
Snicolas

객체를 조롱한다는 것은 실제 객체를 만드는 것을 의미합니까? 조롱이 실제 객체를 만들지 않는다면 왜 클래스가 싱글 톤인지 또는 메소드가 정적인지 중요합니까?
Arun Raaj

45

클러스터링과 관련하여 싱글 톤도 좋지 않습니다 . 따라서 더 이상 응용 프로그램에 "정확히 하나의 싱글 톤"이 없습니다.

다음 상황을 고려하십시오. 개발자는 데이터베이스에 액세스하는 웹 애플리케이션을 작성해야합니다. 동시 데이터베이스 호출이 서로 충돌하지 않도록하려면 스레드 저장을 작성하십시오 SingletonDao.

public class SingletonDao {
    // songleton's static variable and getInstance() method etc. omitted
    public void writeXYZ(...){
        synchronized(...){
            // some database writing operations...
        }
    }
}

따라서 응용 프로그램에는 하나의 싱글 톤 만 존재하고 모든 데이터베이스는이 하나만 통과해야합니다 SingletonDao. 프로덕션 환경은 이제 다음과 같습니다. 싱글 싱글턴

지금까지 모든 것이 정상입니다.

이제 클러스터에 여러 웹 애플리케이션 인스턴스를 설정하려고합니다. 이제 갑자기 다음과 같은 것이 있습니다.

많은 싱글 톤

이상하게 들리지만 이제는 응용 프로그램에 많은 싱글 톤이 있습니다 . 이것이 바로 싱글 톤이 아니어야하는 것입니다. 이 예제에서 볼 수 있듯이 데이터베이스에 동기화 된 호출을하려는 경우 특히 나쁩니다.

물론 이것은 싱글 톤의 나쁜 사용법의 예입니다. 그러나이 예제의 메시지는 다음과 같습니다. 응용 프로그램에 특히 단일 클러스터의 인스턴스가 하나만 있다고 믿을 수는 없습니다.


4
싱글 톤을 구현하는 방법을 모른다면 그렇게하지 않아야합니다. 당신이 무엇을하고 있는지 모른다면, 당신은 그것을 먼저 찾아야하고, 그 후에 만 ​​필요한 것을해야합니다.
Dainius

3
이것은 흥미 롭습니다. 질문이 있습니다. 그렇다면 각각 다른 머신 / JVM에있는 싱글 톤이 단일 데이터베이스에 연결되어 있다면 정확히 어떤 문제가 있습니까? 싱글 톤 범위는 특정 JVM에만 해당되며 클러스터에서도 마찬가지입니다. 철학적으로 말해서이 특정 상황은 우리의 의도가 응용 프로그램 전체의 단일 대상이기 때문에 나쁘다는 것보다는이 배열 때문에 발생할 수있는 기술적 문제를 보게되어 기쁩니다.
Saurabh Patil

39
  1. 전역 변수로 쉽게 사용됩니다.
  2. 싱글 톤에 의존하는 클래스는 상대적으로 단위 테스트가 상대적으로 어렵습니다.

33

독점은 악마이며 읽기 불가능 / 변경 불가능한 상태의 싱글 톤은 '실제'문제입니다 ...

싱글 톤이 병리학적인 거짓말 쟁이 임을 읽은 후 , Jason의 답변 에서 제안한대로 싱글 톤이 종종 오용 되는 방법에 대한 최상의 예를 제공하는이 작은 음식을 발견했습니다 .

다음과 같은 이유로 글로벌이 나쁩니다.

  • ㅏ. 네임 스페이스 충돌이 발생합니다
  • 비. 부주의 한 방식으로 국가를 폭로합니다

싱글 톤에 관해서

  • ㅏ. 명시적인 OO 방식으로 호출하면 충돌이 방지되므로 a를 가리 킵니다. 문제가 아니다
  • 비. 상태가없는 싱글 톤 (공장과 같은)은 문제가되지 않습니다. 상태가있는 싱글 톤은 다시 두 가지 범주에 속할 수 있는데,이 범주는 변경 불가능하거나 한 번 쓰고 많은 구성 (속성 파일)을 읽습니다. 이것들은 나쁘지 않습니다. 참조 홀더의 일종 인 가변 싱글 톤 (Mutable Singleton)이 당신이 말하는 것입니다.

마지막 진술에서 그는 블로그의 '싱글 톤은 거짓말 쟁이이다'라는 개념을 언급하고있다.

이것이 독점에 어떻게 적용됩니까?

독점 게임을 시작하려면 먼저 :

  • 모든 사람들이 같은 페이지에 있도록 규칙을 먼저 설정합니다
  • 게임이 시작될 때 모두에게 동등한 시작이 주어집니다
  • 혼란을 피하기 위해 하나의 규칙 세트 만 제공됩니다.
  • 게임 내내 규칙을 변경할 수 없습니다

자,하지 않은 사람을 위해 정말 독점을했다,이 표준은 최상의 이상적입니다. 독점에 대한 패배는 삼키기가 어렵습니다. 독점은 돈에 관한 것입니다. 당신이 잃어 버리면 나머지 플레이어가 게임을 끝내는 것을 힘들게 지켜봐야하며, 손실은 대개 신속하고 파쇄됩니다. 따라서 규칙은 일반적으로 특정 시점에서 다른 플레이어를 희생하여 일부 플레이어의 자기 이익을 제공하기 위해 왜곡됩니다.

친구 밥, 조, 에드와 독점을하고 있습니다. 빠르게 제국을 건설하고 지수 비율로 시장 점유율을 소비하고 있습니다. 상대방이 약해지고 혈액 냄새가 나기 시작합니다 (그림 참조). 버디 밥은 가능한 한 많은 가치가 낮은 부동산을 그리드 락에 넣었지만, 예상대로 높은 투자 수익을 얻지 못했습니다. 불운의 일격으로 밥은 보드 워크에 착륙하여 게임에서 제외됩니다.

이제 게임은 친절한 주사위 굴림에서 진지한 사업으로 진행됩니다. 밥은 실패의 모범이되었으며 조와 에드는 '그 사람'처럼되기를 원하지 않습니다. 따라서, 당신은 최고의 선수가되어 갑자기 적이됩니다. Joe와 Ed는 테이블 밖에서 거래, 뒤에서 돈을 주입, 저평가 된 주택 스와핑 및 일반적으로 그들 중 하나가 정상에 올 때까지 당신을 약화시키는 것을 연습하기 시작합니다.

그런 다음 그중 하나가 승리하는 대신 프로세스가 완전히 시작됩니다. 갑자기 유한 한 규칙이 움직이는 목표가되고 게임은 생존자 이후 모든 높은 등급의 현실 TV 쇼의 기초를 구성하는 사회적 상호 작용의 유형으로 변질됩니다. 왜, 규칙이 바뀌고 있으며 어떻게 / 왜 / 어떻게 대표해야하는지에 대한 합의가 없기 때문에 더 중요한 것은 결정을 내릴 사람이 없다는 것입니다. 그 시점에서 게임의 모든 플레이어는 자신의 규칙을 만들고 있으며, 두 명의 플레이어가 너무 피곤하여 차드를 유지하고 천천히 포기할 때까지 혼란을 겪습니다.

따라서 게임의 룰북이 싱글 톤을 정확하게 나타내면 독점 룰북이 남용의 예가됩니다.

이것이 프로그래밍에 어떻게 적용됩니까?

변경 가능한 싱글 톤에서 발생하는 명백한 스레드 안전성 및 동기화 문제를 제외하고 ... 데이터 세트가 하나 인 경우 여러 다른 소스에서 동시에 읽거나 조작 할 수 있으며 애플리케이션 실행 수명 동안 존재합니다. 아마도 물러서서 "여기에 올바른 유형의 데이터 구조를 사용하고 있습니까?"라고 물을 때가 좋습니다.

개인적으로, 나는 프로그래머가 응용 프로그램 내에서 일종의 꼬인 크로스 스레드 데이터베이스 저장소로 사용하여 싱글 톤을 남용하는 것을 보았습니다. 코드를 직접 연구 한 결과, 느리게 (스레드 안전에 필요한 모든 스레드 잠금으로 인해) 느리게 작동하고 악의적 인 작업 (예측할 수없는 / 간헐적 인 동기화 버그 때문에)을 증명할 수 있습니다. '생산'조건에서 테스트하기가 거의 불가능합니다. 물론 성능 문제 중 일부를 극복하기 위해 폴링 / 시그널링을 사용하여 시스템을 개발할 수 있었지만 테스트와 관련된 문제를 해결하지 못하고 왜 '실제'데이터베이스가 훨씬 더 강력한 기능으로 동일한 기능을 이미 수행 할 수 있는지 고민하는 이유 / 확장 가능한 방식.

싱글 톤은 싱글 톤이 제공하는 것이 필요한 경우 에만 옵션입니다. 개체의 1 회 읽기 전용 인스턴스입니다. 동일한 규칙이 객체의 속성 / 멤버에도 연결되어야합니다.


1
싱글 톤이 일부 데이터를 현금화하면 어떻게됩니까?
Yola

@Yola 싱글 톤이 미리 정해진 일정 및 / 또는 고정 된 일정으로 업데이트되도록 예약 된 경우 쓰기 수를 줄임으로써 스레드 경합이 줄어 듭니다. 그럼에도 불구하고 동일한 사용법을 시뮬레이션하는 단일 인스턴스가 아닌 인스턴스를 조롱하지 않으면 싱글 톤과 상호 작용하는 코드를 정확하게 테스트 할 수 없습니다. TDD 사람들은 아마도 적합 할 것이지만 효과가있을 것입니다.
Evan Plaice

@EvanPlaice : 모의를 사용하더라도 코드에 문제가있을 수 있습니다 Singleton.getInstance(). 리플렉션을 지원하는 언어는 One True 인스턴스가 저장된 필드를 설정하여 문제를 해결할 수 있습니다. 그러나 IMO는 다른 클래스의 개인 상태로 원숭이를 놀러 가면 테스트가 약간 덜 신뢰할 수 있습니다.
cHao

28

싱글 톤은 싱글 인스턴스가 아닙니다!

다른 답변과 달리 Singletons의 문제점에 대해 이야기하고 싶지 않지만 올바르게 사용될 때 얼마나 강력하고 훌륭했는지 보여주기 위해!

  • 문제 : 멀티 스레딩 환경에서 싱글 톤이 문제 가 될 수 있습니다.
    해결책 : 단일 스레드 부트 스트랩 프로세스를 사용하여 싱글 톤의 모든 종속성을 초기화하십시오.
  • 문제 : 싱글 톤을 조롱하기는 어렵다.
    솔루션 : 조롱을위한 메소드 팩토리 패턴 사용

에 매핑 MyModel할 수 있습니다TestMyModel 때 상속이 사방 클래스 MyModel주입 될 것입니다 당신이 얻을 것이다 TestMyModelinstread. - 문제 : 싱글 톤은 절대로 폐기되지 않아 메모리 누수를 일으킬 수 있습니다.
해결책 : 음, 폐기하십시오! 싱글 톤을 올바르게 처리하기 위해 앱에서 콜백을 구현하면 연결된 데이터를 모두 제거하고 마지막으로 팩토리에서 제거해야합니다.

제목에서 언급했듯이 싱글 톤은 단일 인스턴스에 관한 것이 아닙니다.

  • 싱글 톤은 가독성을 향상시킵니다 . 클래스를 살펴보고 어떤 싱글 톤이 어떤 종속성인지 파악하여 종속성을 파악할 수 있습니다.
  • 싱글 톤으로 유지 보수 향상 . 한 번 클래스에서 종속성을 제거한 후 싱글 톤 주입을 삭제하면 종속성을 이동시킨 다른 클래스의 큰 링크를 편집 할 필요가 없습니다 (이 냄새 나는 코드 @Jim Burger )
  • 싱글 톤은 메모리와 성능을 향상시킵니다 . 어플리케이션에서 어떤 일이 발생하고 콜백이 오래 걸리면 싱글 톤을 사용하여 중개인을 줄이고 메모리와 성능을 낭비하고 성능과 메모리 사용량을 향상시킵니다 ( 불필요한 지역 변수 할당을 피함으로써).

2
이것은 Singletons의 주요 문제를 해결하지 못하므로 프로젝트의 수천 가지 클래스 중 하나에서 전역 상태에 액세스 할 수 있습니다.
LegendLength

8
그것이 목적이 될 것입니다.
Ilya Gazman

LegendLength 왜 잘못 되었나요? 예를 들어, 내 응용 프로그램에는 Settings모든 위젯에서 액세스 할 수 있는 단일 객체가 있으므로 각 위젯은 표시된 숫자의 형식을 지정하는 방법에 대해 알 수 있습니다. 전역 적으로 액세스 가능한 객체가 아닌 경우 생성자의 각 위젯에 생성자에 삽입하고 참조를 멤버 변수로 유지해야합니다. 이것은 끔찍한 기억과 시간 낭비입니다.
VK

23

싱글 톤이 어떻게 나쁜지에 대한 나의 대답은 항상 "올바른 일을하기가 어렵다"입니다. 언어의 많은 기본 구성 요소는 싱글 톤 (클래스, 함수, 네임 스페이스 및 심지어 연산자)이며 컴퓨팅의 다른 측면 (로컬 호스트, 기본 경로, 가상 파일 시스템 등)의 구성 요소와 마찬가지로 우연이 아닙니다. 그들은 때때로 문제와 좌절을 야기하지만, 많은 일들이 훨씬 더 잘 작동하도록 만들 수 있습니다.

내가 볼 수있는 가장 큰 두 가지 단점은 전역처럼 취급하고 싱글 톤 클로저를 정의하지 못하는 것입니다.

모든 사람들은 기본적으로 싱글 톤을 글로벌로 이야기합니다. 그러나, 지구상에서 많은 (슬프게도, 전부는 아님) 악의는 본질적으로 세계적이라는 것이 아니라 어떻게 사용 하는가에 달려 있습니다. 싱글 톤도 마찬가지입니다. 실제로 "단일 인스턴스"가 실제로 "전 세계적으로 액세스 가능"을 의미 할 필요는 없습니다. 그것은 더 자연스러운 부산물이며, 우리가 알고있는 모든 나쁜 점을 감안할 때, 우리는 전 세계의 접근성을 이용하기 위해 서둘러서는 안됩니다. 프로그래머가 싱글 톤을 본 후에는 항상 인스턴스 메소드를 통해 직접 액세스하는 것 같습니다. 대신 다른 개체와 마찬가지로 탐색해야합니다. 대부분의 코드는 싱글 톤 (느슨한 커플 링, 맞습니까?)을 다루고 있다는 것을 인식해서는 안됩니다. 전역 코드처럼 작은 코드 만 객체에 액세스하면 많은 피해가 발생하지 않습니다.

싱글 톤 컨텍스트도 매우 중요합니다. 싱글 톤의 정의 특성은 "하나만"있다는 것이지만, 어떤 맥락 / 네임 스페이스 내에서 "하나만"이라는 것이 진실입니다. 일반적으로 스레드, 프로세스, IP 주소 또는 클러스터 당 하나이지만 프로세서, 컴퓨터, 언어 네임 스페이스 / 클래스 로더 / 무엇이든, 서브넷, 인터넷 등 하나 일 수 있습니다.

덜 흔한 또 다른 실수는 싱글 톤 라이프 스타일을 무시하는 것입니다. 단 하나만 있다고해서 Singleton이 "항상 존재하고 항상있을 것"이라는 전능함을 의미하지는 않으며 일반적으로 바람직하지도 않습니다 (시작과 끝이없는 객체는 코드에서 모든 종류의 유용한 가정을 위반하므로 사용되어야합니다) 가장 절박한 상황에서.

이러한 실수를 피하면 Singletons는 여전히 PITA가 될 수 있습니다. 비트는 최악의 많은 문제가 크게 완화 될 수 있습니다. 정의 된 생성 및 소멸 방법과 호출시기 및 방법을 결정하는 수명주기와 "인스턴스"메소드의 수명주기를 사용하여 클래스 로더 당 한 번 (스레드 안전성 정책이 필요함) 명시 적으로 정의 된 Java Singleton을 상상해보십시오. 패키지 보호를 통해 일반적으로 다른 비전 역 객체를 통해 액세스합니다. 여전히 잠재적 인 문제의 원천이지만 확실히 훨씬 적은 문제입니다.

슬프게도, 싱글 톤을하는 방법에 대한 좋은 예를 가르치기보다는. 우리는 나쁜 예를 가르치고, 프로그래머가 잠시 동안 사용하지 못하게하고, 잘못된 디자인 패턴이라고 말합니다.


23

Wikipedia Singleton_pattern 참조

또한 일부 사람들에 의해 반 패턴으로 간주되며, 과도하게 사용되었다고 느끼고 클래스의 유일한 인스턴스가 실제로 필요하지 않은 상황에서 불필요한 제한을 초래합니다. [1] [2] [3] [4]

참조 (기사에서 관련 참조 만)

  1. ↑ Alex Miller. 내가 싫어하는 패턴 # 1 : Singleton , 2007 년 7 월
  2. ^ Scott Densmore. 싱글 톤이 악한 이유 , 2004 년 5 월
  3. ^ Steve Yegge. 싱글 톤은 어리석은 것으로 간주 , 2004 년 9 월
  4. ^ JB Rainsberger, IBM. 2001 년 7 월, 싱글 톤을 현명하게 사용하십시오

8
패턴에 대한 설명은 왜 그것이 악으로 간주되는지 설명하지 않습니다 ...
Jrgns

2
거의 공평하지 않다 : "일부 수업이 실제로 필요하지 않은 상황에서 불필요한 사용을 제한하여 과도하게 사용되는 느낌이 드는 일부 사람들은 반 패턴으로 간주된다." 참조를보십시오 ... 어쨌든 RTFM이 있습니다.
GUI Junkie

17

싱글 톤 자체가 나쁘지는 않지만 GoF 디자인 패턴은 아닙니다. 실제로 유일하게 유효한 주장은 GoF 디자인 패턴이 테스트와 관련하여, 특히 테스트가 병렬로 실행되는 경우에는 적합하지 않다는 것입니다.

코드에서 다음 수단을 적용하는 한 클래스의 단일 인스턴스를 사용하는 것이 올바른 구성입니다.

  1. 싱글 톤으로 사용될 클래스가 인터페이스를 구현하는지 확인하십시오. 이를 통해 동일한 인터페이스를 사용하여 스텁 또는 모의를 구현할 수 있습니다.

  2. 싱글 톤이 스레드로부터 안전해야합니다. 그것은 주어진 것입니다.

  3. 싱글 톤은 본질적으로 단순해야하고 지나치게 복잡해서는 안됩니다.

  4. 단일 객체를 지정된 객체로 전달해야하는 응용 프로그램을 실행하는 동안 해당 객체를 빌드하는 클래스 팩토리를 사용하고 클래스 팩토리가 싱글 톤 인스턴스를 필요한 클래스로 전달하도록합니다.

  5. 테스트하는 동안 그리고 결정적인 동작을 보장하기 위해 싱글 톤 클래스를 실제 클래스 자체 또는 별도의 인스턴스로 동작을 구현하는 스텁 / mock으로 만들고이를 필요한 클래스에 그대로 전달하십시오. 테스트 중에 싱글 톤이 필요한 테스트 대상 객체를 생성하는 클래스 팩터를 사용하지 마십시오. 단일 글로벌 인스턴스를 통과하므로 목적을 상실합니다.

우리는 솔루션에서 싱글 톤을 사용하여 병렬 테스트 실행 스트림에서 결정적인 동작을 보장 할 수있는 많은 성공을 거두었습니다.


+1, 마지막으로 싱글 톤이 유효 할 해결되는 답변 .
Pacerier

16

허용 된 답변에서 4 점을 다루고 싶습니다. 아마도 누군가 내가 왜 잘못했는지 설명 할 수 있기를 바랍니다.

  1. 코드에 의존성을 숨기는 것은 왜 나쁜가? 이미 수십 개의 숨겨진 종속성 (C 런타임 호출, OS API 호출, 전역 함수 호출)이 있으며 싱글 톤 종속성을 쉽게 찾을 수 있습니다 (예 : search ()).

    "전세계를 통과하지 못하도록하는 것은 코드 냄새입니다." 싱글 톤 코드 냄새를 피하기 위해 왜 무언가를 전달하지 않습니까?

    단일 톤을 피하기 위해 호출 스택에서 10 개의 함수를 통해 객체를 전달하는 경우 그렇게 훌륭합니까?

  2. 단일 책임 원칙 : 나는 이것이 약간 모호하며 귀하의 책임 정의에 달려 있다고 생각합니다. 관련 질문은 왜이 특정 "책임"을 수업에 추가 하는 것입니까?

  3. 왜 객체를 클래스에 전달하면 해당 객체를 클래스 내에서 싱글 톤으로 사용하는 것보다 더 밀접하게 결합됩니까?

  4. 상태가 지속되는 시간이 왜 변경됩니까? 싱글 톤은 수동으로 생성하거나 파괴 할 수 있으므로 컨트롤은 여전히 ​​존재하며 수명은 싱글 톤이 아닌 오브젝트의 수명과 동일하게 만들 수 있습니다.

단위 테스트와 관련하여 :

  • 모든 수업을 단위 테스트 할 필요는 없습니다.
  • 단위 테스트가 필요한 모든 클래스가 싱글 톤의 구현을 변경해야하는 것은 아닙니다.
  • 그들이 경우 어떻게 단위 테스트 및 구현을 변경해야 할 필요, 그것은 의존성 주입을 통해 전달 된 싱글을 필요로 싱글 톤을 사용하는 클래스를 쉽게 변경할 수 있습니다.

1
1. 숨겨진 모든 종속성? 그것들도 나쁘다. 숨겨진 의존성은 항상 악하다. 그러나 CRT 및 OS의 경우 이미 존재하며 무언가를 수행 할 수있는 유일한 방법입니다. (런타임이나 OS를 사용하지 않고 C 프로그램을 작성해보십시오.) 그 일을하는 능력은 그들의 부정적인 것보다 훨씬 큽니다. 우리 코드의 싱글 톤은 그런 사치를 얻지 못한다. 그것들은 우리의 통제 및 책임 영역 내에 단단히 있기 때문에 모든 사용 (읽기 : 모든 추가 숨겨진 종속성)은 작업을 수행하는 유일한 합리적인 방법으로 정당화되어야합니다. 아주, 아주 그들 중 몇 가지 사실입니다.
cHao

2. "책임"은 일반적으로 "변경 사유"로 정의됩니다. 싱글 톤은 수명을 관리하고 실제 업무를 수행합니다. 작업 수행 방식 또는 객체 그래프 작성 방식을 변경하는 경우 클래스를 변경해야합니다. 그러나 다른 코드가 객체 그래프를 작성하고 클래스가 실제 작업을 수행하는 경우 해당 초기화 코드는 원하는대로 객체 그래프를 설정할 수 있습니다. 테스트 복식을 삽입하고 마음대로 모든 것을 찢을 수 있으며 더 이상 숨겨진 움직이는 부품에 의존하지 않기 때문에 모든 것이 훨씬 모듈화되고 테스트 가능합니다.
cHao

1
@cHao : 1. "일을 할 수있는 능력이 그들의 부정적인 것보다 훨씬 크다"는 것을 잘 알고 있습니다. 로깅 프레임 워크가 그 예입니다. 개발자가 로깅을 권장하지 않는 경우 함수 호출 계층을 통한 종속성 주입으로 전역 로깅 오브젝트를 전달하도록 지시하는 것은 좋지 않습니다. 따라서 싱글 톤. 3. 클래스의 클라이언트가 다형성을 통해 동작을 제어하도록하려는 경우 밀접한 연결 지점이 적합합니다. 종종 그렇지 않습니다.
Jon

2
4. "수동으로 파괴 할 수 없음"이 "하나의 인스턴스 만 될 수 있음"의 논리적 의미가 확실하지 않습니다. 그것이 싱글 톤을 정의하는 방법이라면, 우리는 같은 것에 대해 이야기하지 않습니다. 모든 수업 단위 테스트를 해서는 안됩니다 . 그것은 사업 결정이며, 때로는 시장 출시 시간 또는 개발 비용이 우선합니다.
Jon

1
3. 다형성을 원하지 않으면 인스턴스가 필요하지 않습니다. 어쨌든 정적 클래스로 만들면 어쨌든 단일 객체와 단일 구현에 자신을 묶게 될 수 있습니다. 적어도 API는 그다지 거짓말하지 않습니다.
cHao

15

빈스 휴스턴 (Vince Huston) 은 다음과 같은 기준을 가지고 있습니다.

다음 기준 중 세 가지가 모두 충족되는 경우에만 싱글 톤을 고려해야합니다.

  • 단일 인스턴스의 소유권을 합리적으로 할당 할 수 없습니다
  • 게으른 초기화가 바람직합니다
  • 달리 글로벌 액세스는 제공되지 않습니다

단일 인스턴스의 소유권, 초기화시기 및 방법 및 전역 액세스에 문제가없는 경우 Singleton은 충분히 흥미롭지 않습니다.


14

싱글 톤은 순수한 관점에서 나쁘다.

실질적인 관점에서 보면 싱글 톤은 개발 시간과 복잡성 사이의 균형을 이룹니다 .

응용 프로그램이 많이 변경되지 않는다는 것을 알고 있다면 함께 갈 수 있습니다. 요구 사항이 예기치 않은 방식으로 변경되는 경우 상황을 리팩토링해야 할 수도 있습니다 (대부분의 경우는 괜찮습니다).

싱글 톤은 때때로 단위 테스트를 복잡하게 만듭니다 .


3
내 경험에 따르면 싱글 톤으로 시작 하면 장기적으로 계산하지 않고 단기적으로도 당신을 해칠 것입니다. 응용 프로그램이 이미 존재하고 이미 다른 싱글 톤에 감염된 경우이 효과는 덜 효과적입니다. 처음부터 시작 하시겠습니까? 모든 비용을 피하십시오! 객체의 단일 인스턴스를 원하십니까? 팩토리가이 객체의 인스턴스화를 관리하도록합니다.
Sven

1
AFAIK 싱글 톤 공장 방법입니다. 따라서 나는 당신의 추천을 얻지 못합니다.
tacone

3
"A factory"! = "같은 클래스의 다른 유용한 것들과 분리 될 수없는 factory_method"
Sven

13

패턴이 본질적으로 단일 인 모델의 일부 측면에 사용된다고 가정하면 패턴에는 본질적으로 아무런 문제가 없습니다.

나는 백래시가 과도하게 사용되어서 이해하고 구현하는 것이 가장 쉬운 패턴이기 때문이라고 생각합니다.


6
나는 반발이 그들이 다루어야 할 악몽임을 깨닫고 공식 단위 테스트를 연습하는 사람들과 더 관련이 있다고 생각합니다.
Jim Burger

1
이것이 왜 -1인지 확실하지 않습니다. 가장 서술적인 답변은 아니지만 잘못도 아닙니다.
무법자 프로그래머

13

나는 good / evil 논쟁에 대해서는 언급하지 않을 것이지만 Spring 이 등장한 이래로 나는 그것들을 사용하지 않았다 . 의존성 주입 을 사용하면 싱글 톤, 서비스 로케이터 및 팩토리에 대한 요구 사항이 거의 제거되었습니다. 적어도 내가하는 일 (Java 기반 웹 응용 프로그램)의 경우 훨씬 생산적이고 깨끗한 환경입니다.


3
스프링 빈은 기본적으로 싱글 톤이 아닌가?
슬림

3
네,하지만 '코딩'싱글 톤을 의미합니다. 나는 '싱글 톤'을 작성하지 않았습니다 (예 : 디자인 패턴에 따라 개인 생성자 yada yada yada로). 그렇습니다. 봄에는 단일 인스턴스를 사용하고 있습니다.
prule

4
다른 사람들이 그것을 할 때 괜찮습니다 (나중에 그것을 사용한다면), 다른 사람들이 그것을 사용하고 그것을 사용하지 않을 경우, 그것은 악한 것입니까?
Dainius

11

싱글 톤은 패턴이며 다른 도구와 마찬가지로 사용하거나 남용 할 수 있습니다.

싱글 톤의 나쁜 부분은 일반적으로 사용자입니다 (또는 싱글 톤이 의도하지 않은 작업에 부적절하게 사용한다고 말해야합니다). 가장 큰 문제는 싱글 톤을 가짜 전역 변수로 사용하는 것입니다.


1
고마워 Loki :) 정확히 내 요점. 마녀 사냥 전체가 고토 토론을 떠올리게합니다. 도구는 사용법을 알고있는 사람들을위한 것입니다. 마음에 들지 않는 도구를 사용하면 위험 할 수 있으므로 피하십시오. 그러나 다른 사람에게 올바르게 사용하는 법을 배우지 말라고 말하지 마십시오. 나는 정확히 "단일"이 아니지만, 나는 그런 도구를 가지고 있고 그것을 사용하기에 적절한 곳을 느낄 수 있다는 사실을 좋아합니다. 기간.
dkellner

8

로거 또는 데이터베이스 연결과 같은 싱글 톤을 사용하여 코드를 작성한 후 둘 이상의 로그 나 둘 이상의 데이터베이스가 필요하다는 것을 알게되면 문제가 발생합니다.

싱글 톤은 그것들을 일반 객체로 옮기기가 매우 어렵습니다.

또한 스레드로부터 안전하지 않은 싱글 톤을 작성하는 것은 너무 쉽습니다.

싱글 톤을 사용하는 대신 필요한 모든 유틸리티 개체를 함수에서 함수로 전달해야합니다. 다음과 같이 모든 것을 도우미 객체로 감싸면 단순화 될 수 있습니다.

void some_class::some_function(parameters, service_provider& srv)
{
    srv.get<error_logger>().log("Hi there!");
    this->another_function(some_other_parameters, srv);
}

4
모든 방법에 대한 논쟁은 훌륭합니다. 최소한 10 가지 방법으로 API를 오염시키는 방법으로 가야합니다.
Dainius

그것은 명백한 단점이며, 언어에 의해 처리 되어야만하는 방법으로, 어떤 식 으로든 중첩 된 호출로 전파되지만 그러한 지원이 없으면 수동으로 수행되어야합니다. 시스템 시계를 나타내는 객체와 같은 자연스러운 싱글 톤조차도 문제가 발생할 수 있습니다. 예를 들어, 테스트로 시계 종속 코드 (여러 관세 전기 계량기 생각)를 어떻게 다룰 것인가?
Roman Odaisky

싱글 톤을 테스트하지 않는 경우 테스트 대상에 따라 달라지며 왜 동작을 신경 쓰는지, 2 개의 원격 객체가 서로 다른 동작에 의존하는 경우 싱글 톤 문제가 아닙니다.
Dainius

어떤 언어를 중첩 호출로 전파 할 수 있습니까?
Dainius

1
하나의 모듈이 다른 모듈에 의존하고 있고 / moc을 알 수없는 경우 싱글 톤인지 여부에 관계없이 힘들 것입니다. 사람들은 상속 방법을 자주 사용해야하는 곳에서 구성을 사용해야하지만 상속이 나쁘다고 말하지 않으며 OOP는 많은 비용을 피해야합니다. 많은 사람들이 거기에서 디자인 실수를하고 있습니까?
Dainius

8

싱글 톤의 문제점은 범위 확대 문제이므로 커플 링 . 단일 인스턴스에 액세스해야하는 상황이 있다는 것을 부인할 수 없으며 다른 방법으로도 달성 할 수 있습니다.

나는 이제 제어 역전을 중심으로 설계하는 것을 선호한다 IoC ( ) 컨테이너를 컨테이너에서 수명을 제어 할 수 있습니다. 이를 통해 인스턴스에 의존하는 클래스의 이점을 통해 단일 인스턴스가 있다는 사실을 알 수 없습니다. 싱글 톤의 수명은 나중에 변경 될 수 있습니다. 그런 예가 최근에 발생하면 단일 스레드에서 다중 스레드로 쉽게 조정할 수있었습니다.

FWIW, 단위 테스트를 시도 할 때 PIA 인 경우 디버깅, 버그 수정 또는 향상하려고 할 때 PIA가됩니다.



7

싱글 톤은 나쁘지 않습니다. 전 세계적으로 독특하지 않은 세계적으로 독특한 것을 만들 때만 나쁩니다.

그러나 "응용 프로그램 범위 서비스"(구성 요소가 상호 작용하게하는 메시징 시스템에 대해 생각할 수 있음)가 있습니다.이 방법은 "SendMessage (...)"메서드가있는 단일 클래스 인 "MessageQueue"에 대한 호출입니다.

그런 다음 모든 곳에서 다음을 수행 할 수 있습니다.

MessageQueue.Current.SendMessage (new MailArrivedMessage (...));

그리고 물론 다음을 수행하십시오.

MessageQueue.Current.RegisterReceiver (this);

IMessageReceiver를 구현하는 클래스에서


6
더 작은 범위의 두 번째 메시지 대기열을 만들려면 코드를 재사용하여 생성 할 수없는 이유는 무엇입니까? 싱글 톤이이를 방지합니다. 그러나 방금 일반 메시지 대기열 클래스를 만든 다음 "응용 프로그램 범위"인스턴스 역할을하는 하나의 전역 인스턴스를 만든 경우 다른 용도로 사용할 두 번째 인스턴스를 만들 수 있습니다. 그러나 클래스를 싱글 톤으로 만들면 두 번째 메시지 큐 클래스 를 작성해야합니다 .
jalf

1
또한 글로벌 CustomerOrderList가 없어 MessageQueue와 같은 곳에서 멋지게 호출 할 수있는 이유는 무엇입니까? 나는 그 대답이 둘 다 동일하다고 생각합니다 : 효과적으로 전역 변수를 만들고 있습니다.
LegendLength

7

너무 많은 사람들이 싱글 톤 패턴으로 스레드 안전하지 않은 객체를 넣습니다. DataContext가 스레드 안전하지 않고 순수하게 작업 단위 객체라는 사실에도 불구하고 Singleton 패턴으로 수행 된 DataContext ( LINQ to SQL )의 예를 보았습니다 .


많은 사람들이 안전하지 않은 멀티 스레드 코드를 작성합니다. 이것이 스레드를 제거해야한다는 의미입니까?
Dainius

@Dainius : 사실 그렇습니다. IMO 기본 동시성 모델은 다중 프로세스 여야하며, 두 프로세스가 (1) 서로 쉽게 메시지를 전달하고 /하거나 (2) 필요에 따라 메모리를 공유 할 수 있습니다. 스레딩은 모든 것을 공유하고 싶을 때 유용 하지만 실제로는 원하지 않습니다. 전체 주소 공간을 공유하여 에뮬레이션 할 수 있지만 안티 패턴으로 간주됩니다.
cHao

@Dainius : 물론 그것은 정직한 대의 동시성이 전혀 필요하지 않다고 가정합니다. 종종 당신이 원하는 것은 OS가 다른 것을하기를 기다리는 동안 한 가지 일을 할 수있는 것입니다. (예를 들어 소켓에서 읽는 동안 UI 업데이트) 적절한 비동기 API는 대부분의 경우 풀 스레딩을 불필요하게 만들 수 있습니다.
cHao

따라서 많은 데이터 매트릭스가 있고 프로세스가 필요한 경우 단일 스레드에서 처리하는 것이 좋습니다. 많은 사람들이 그 작업을 잘못 수행 할 수 있기 때문입니다.
Dainius

@Dainius : 많은 경우에 그렇습니다. ( 믹스에 동기화를 추가 하면 스레딩이 실제로 속도를 늦출 수 있습니다 . 이것이 멀티 스레딩이 대부분의 사람들이 가장 먼저 생각하지 않아야하는 주요 예입니다.) 다른 경우에는 두 가지 프로세스를 공유하는 것이 좋습니다 필요한 것들. 물론 프로세스가 메모리를 공유 할 수 있도록 준비해야합니다. 그러나 솔직히, 나는 같은 것을 볼 좋은의 일 - 훨씬 더, 적어도, 공유 - 모든 모드로 디폴트보다. 공유 할 부분과 스레드로부터 안전해야하는 부분을 명시 적으로 말하고 (이상적으로는 알아야 함) 요구합니다.
cHao

6

아직 아무도 말하지 않은 싱글 톤에 대해 한 가지 더 있습니다.

대부분의 경우 "단일성"은 인터페이스의 특성이 아니라 일부 클래스에 대한 세부적인 구현입니다. 컨트롤 컨테이너의 반전은이 특성을 클래스 사용자로부터 숨길 수 있습니다. 클래스를 싱글 톤으로 표시하면됩니다.@Singleton 예 : Java 주석 포함) 그게 전부입니다. IoCC가 나머지 작업을 수행합니다. 액세스가 이미 IoCC에 의해 관리되므로 싱글 톤 인스턴스에 대한 글로벌 액세스를 제공 할 필요가 없습니다. 따라서 IoC 싱글 톤에는 아무런 문제가 없습니다.

IoC 싱글 톤과 반대되는 GoF 싱글 톤은 getInstance () 메소드를 통해 인터페이스에 "단일성"을 노출 시키므로 위에서 언급 한 모든 문제를 겪게됩니다.


3
제 생각에는 "단일성"은 런타임 환경 세부 사항이므로 클래스 코드를 작성한 프로그래머가 고려해서는 안됩니다. 오히려 USER 클래스가 고려해야합니다. USER만이 실제로 필요한 인스턴스 수를 알고 있습니다.
어스 엔진

5

싱글 톤 적절 하고 최소한으로 사용한다면 악한 것이 아닙니다 . 어떤 시점에서 싱글 톤의 요구를 대체하는 다른 좋은 디자인 패턴이 많이 있습니다 (또한 최상의 결과를 제공합니다). 그러나 일부 프로그래머는 그러한 좋은 패턴을 인식하지 못하고 싱글 톤을 악용하는 모든 경우에 싱글 톤을 사용합니다.


대박! 완전히 동의하십시오! 그러나 훨씬 더 정교하게 만들 수있을 것입니다. 가장 일반적으로 무시되는 디자인 패턴을 확장하고 싱글 톤을 "적절하고 최소한으로"사용하는 방법 등이 있습니다. 완료 보다 더 쉽게 말했다 ! : P
cregox

기본적으로 대안은 전역 상태를 통해 객체에 액세스하는 대신 메서드 매개 변수를 통해 객체를 전달하는 것입니다.
LegendLength

5

첫째, 수업과 협력자들은 먼저 부양 가족에게 초점을 맞추기보다는 의도 된 목적을 수행해야합니다. 수명주기 관리 (인스턴스 생성시 및 범위를 벗어난 경우)는 클래스 책임의 일부가되어서는 안됩니다. 이에 대한 가장 좋은 방법은 종속성 주입을 사용하여 종속성을 관리하기 위해 새 구성 요소를 만들거나 구성하는 것입니다.

종종 소프트웨어가 복잡 해져서 상태가 다른 Singleton 클래스의 여러 독립 인스턴스를 갖는 것이 합리적입니다. 이러한 경우 싱글 톤을 잡기 위해 코드를 커밋하는 것은 잘못입니다. 사용Singleton.getInstance()작은 간단한 시스템에서는 이 가능하지만 동일한 클래스의 다른 인스턴스가 필요할 때 작동하지 않거나 확장되지 않습니다.

어떤 클래스도 싱글 톤으로 생각해서는 안되지만, 클래스의 사용법이나 종속 항목을 구성하는 데 사용되는 방법이어야합니다. 빠르고 불쾌한 경우에는 이것이 중요하지 않습니다. 단지 하드 코딩은 파일 경로가 중요하지 않다고 말하지만 더 큰 응용 프로그램의 경우 DI를 사용하여 이러한 종속성을 더 적절하게 제거하고 관리해야합니다.

테스트에서 싱글 톤이 발생하는 문제는 하드 코딩 된 단일 사용 사례 / 환경의 증상입니다. 테스트 스위트와 많은 테스트는 각각 개별적이며 단일 코드 하드 코딩과 호환되지 않는 무언가를 분리합니다.


4

기본적으로 객체 지향 전역 변수이므로 필요하지 않은 방식으로 클래스를 디자인 할 수 있습니다.


클래스가 한 번의 인스턴스로 제한되지 않는 경우 거의 동일한 결과가 나오는 세마포어로 관리되는 클래스의 정적 멤버가 필요합니다! 제안 된 대안은 무엇입니까?
Jeach

"MainScreen"을 사용하는 거대한 응용 프로그램이 있습니다.이 화면에는 더 작은 모달 / 비 모달 창 / UI 양식이 열립니다. IMHO 나는 MainScreen이 싱글 톤이어야한다고 생각합니다. 예를 들어 응용 프로그램의 가장 먼 곳에있는 위젯은 MainScreen의 상태 표시 줄에 상태를 표시하려고합니다. MainScreen.getInstance (). setStatus ( "일부 텍스트"); 대안으로 무엇을 제안합니까? 응용 프로그램 전체에 MainScreen을 전달하십시오 ?? : D
Salvin Francis

2
@ SalvinFrancis : 내가 추천하는 것은, 신경 쓰지 않아야 할 물건에 관심이있는 물건을 가지고 있지 않고 앱을 가로 질러 서로 엉망이되는 것을 멈추는 것입니다. :) 귀하의 예는 이벤트로 훨씬 잘 수행됩니다. 이벤트를 올바르게 수행하면 위젯은 MainScreen이 있는지 여부를 신경 쓰지 않아도됩니다. 그것은 단지 "이봐, 일이 일어났다"를 방송 하고, "일이 일어났다"이벤트를 구독 한 모든 것 (메인 스크린, WidgetTest 또는 다른 것)은 어떻게 반응하고 싶은지를 결정합니다. OOP는 방법의 것을 가정은 어쨌든 수행 할 수 있습니다. :)
cHao

1
@Salvin 많은 컴포넌트에 의해 '자동'업데이트되는 경우 디버깅 할 때 MainScreen에 대해 추론하기가 얼마나 어려운지 고려하십시오. 귀하의 예는 싱글 톤이 나쁜 완벽한 이유입니다.
LegendLength
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.