싱글 톤 : 어떻게 사용해야합니까


291

편집 : 다른 질문에서 나는 싱글 톤에 대한 많은 질문 / 답변으로 연결되는 답변을 제공했습니다 : 싱글 톤에 대한 자세한 정보는 여기 :

그래서 Singletons 스레드를 읽었습니다 : 좋은 디자인 또는 목발?
그리고 논쟁은 여전히 ​​분노합니다.

싱글 톤을 디자인 패턴으로보고 있습니다 (좋고 나쁨).

싱글 톤의 문제는 패턴이 아니라 사용자 (죄송합니다)입니다. 모두와 그들의 아버지는 그들이 정확하게 하나를 구현할 수 있다고 생각합니다. 또한 모두가 올바른 싱글 톤을 구현할 수 있다고 생각하기 때문에 패턴을 남용하여 적절하지 않은 상황에서 사용합니다 (글로벌 변수를 싱글 톤으로 대체!).

따라서 대답해야 할 주요 질문은 다음과 같습니다.

  • 언제 싱글 톤을 사용해야합니까
  • 싱글 톤을 올바르게 구현하는 방법

이 기사에 대한 희망은 싱글 톤을 올바르게 사용하는시기와 방법에 대한 권위있는 출처 (여러 사이트를 검색하고 검색하지 않고)를 한 곳에 모을 수 있기를 희망합니다. 또한 사용하지 않는 이유와 좋은 구현의 약점을 설명하는 안티 사용 및 일반적인 나쁜 구현 목록이 적합합니다.


공을 굴리십시오.
나는 손을 잡고 이것이 내가 사용하지만 문제가 있다고 말합니다.
나는 그의 책 "Effective C ++"에서 주제를 다루는 "Scott Myers"를 좋아한다

싱글 톤을 사용하는 좋은 상황 :

  • 로깅 프레임 워크
  • 스레드 재활용 풀
/*
 * C++ Singleton
 * Limitation: Single Threaded Design
 * See: http://www.aristeia.com/Papers/DDJ_Jul_Aug_2004_revised.pdf
 *      For problems associated with locking in multi threaded applications
 *
 * Limitation:
 * If you use this Singleton (A) within a destructor of another Singleton (B)
 * This Singleton (A) must be fully constructed before the constructor of (B)
 * is called.
 */
class MySingleton
{
    private:
        // Private Constructor
        MySingleton();
        // Stop the compiler generating methods of copy the object
        MySingleton(MySingleton const& copy);            // Not Implemented
        MySingleton& operator=(MySingleton const& copy); // Not Implemented

    public:
        static MySingleton& getInstance()
        {
            // The only instance
            // Guaranteed to be lazy initialized
            // Guaranteed that it will be destroyed correctly
            static MySingleton instance;
            return instance;
        }
};

확인. 비판과 다른 구현을 함께합시다.
:-)


36
나중에 여러 로거를 원한다면 어떻게해야합니까? 아니면 여러 스레드 풀? 하나의 로거 만 원하는 경우 하나의 인스턴스 만 작성하여 글로벌로 만드십시오. 싱글 톤은 절대적으로 하나만 있어야하고 세계적으로 IMHO가 필요한 경우에만 좋습니다.

3
누가 프레임 워크는 1 개의 로거 인스턴스 만 가질 수 있다고 말했다. 프레임 워크를 나타내는 하나의 싱글 톤. Framwork는 특정 로거를 제공 할 수 있습니다.
Martin York

네. 나는 singeltong을 스레드 풀로 사용하지 않을 것입니다. 답을 촉발시키는 아이디어를 던지기 만하면됩니다.
Martin York

전략 패턴을 구현하는 @Dan Singleton. 동작은 싱글 톤에서 추상화됩니다. 싱글 톤은 단일 진입 점입니다. 두 개의 로거가없고, 로그 방법을 결정할 수있는 하나의 로거가 있습니다. 한 번에 하나의 로그에만 출력 할 수 없으며 두 개가 없어도됩니다.
리 루비 에르

5
Xaade : 두 파일에 로그하려면 어떻게해야합니까? 아니면 데이터베이스에? 아니면 네트워크 소켓? 아니면 GUI 위젯? 요점은 인공적인 제한을 추가하지 않아도된다는 것입니다. 실수로 단 하나가 아닌 두 개의 for 루프를 만든 적이 있습니까? 하나의 로거 만 원하는 경우 하나만 작성하십시오.

답변:


181

모두 잘못되었습니다. 질문을 읽으십시오. 대답:

다음과 같은 경우 싱글 톤을 사용하십시오.

  • 시스템에 하나의 유형의 객체가 하나만 있어야합니다.

다음과 같은 경우 싱글 톤을 사용하지 마십시오.

  • 메모리를 절약하고 싶습니다
  • 새로운 것을 시도하고 싶다
  • 당신은 당신이 알고 얼마나 과시하고 싶어
  • 다른 모든 사람들이 그렇게하기 때문에 ( wikipedia의 화물 컬트 프로그래머 참조 )
  • 사용자 인터페이스 위젯에서
  • 캐시 여야합니다
  • 문자열에서
  • 세션에서
  • 하루 종일 갈 수있어

최고의 싱글 톤을 만드는 방법 :

  • 작을수록 좋습니다. 나는 미니멀리스트입니다
  • 나사산이 안전한지 확인하십시오
  • 절대 null이 아닌지 확인하십시오
  • 한 번만 작성하십시오
  • 게으른 또는 시스템 초기화? 요구 사항까지
  • 때때로 OS 또는 JVM이 싱글 톤을 생성합니다 (예 : Java에서 모든 클래스 정의는 싱글 톤임)
  • 소멸자를 제공하거나 어떻게 든 자원을 처분하는 방법을 알아냅니다.
  • 적은 메모리 사용

14
사실, 나는 당신도 옳지 않다고 생각합니다. " 시스템에 한 유형의 객체를 하나만 가지고 있고 전역으로 액세스 할 수 있어야 하는 경우"라고 말하면됩니다. 필요 에 중점을두고 있습니다. 편리한 경우에는하지 마십시오. 반드시 있어야합니다.

91
당신도 틀렸어요 하나의 개체 만 필요한 경우 하나만 만듭니다. 응용 프로그램을 돌이킬 수 없게 손상시키지 않고 두 인스턴스를 수용 할 수있는 논리적 방법이없는 경우 단일 인스턴스로 만드는 것을 고려해야합니다. 그리고 또 다른 측면 인 글로벌 액세스가 있습니다. 인스턴스에 대한 글로벌 액세스가 필요하지 않은 경우 싱글 톤이 아니어야합니다.
jalf

4
수정을 위해 닫히고 확장을 위해 엽니 다. 문제는 싱글 톤을 듀오 톤 또는 트리플 톤으로 확장 할 수 없다는 것입니다. 싱글 톤으로 붙어 있습니다.
리 루비 에르

2
@ enzom83 : capital-S Singleton에는 단일성을 보장하는 코드가 포함되어 있습니다. 당신은 단지 하나 개의 인스턴스를 원하는 경우에, 당신은 코드를 잃을 수 단순히 당신이 단일 인스턴스의 메모리 절감 효과를 제공합니다 ... 하나 개의 인스턴스를 직접 생성, 플러스 독신 - 수행 코드의 obviation에서 저축 - 또한 희생하지 의미 요구 사항이 변경 될 경우 두 번째 인스턴스를 만들 수 있습니다.
cHao

4
"시스템에 하나의 유형의 객체가 하나만 있어야하는 경우"- "... 단일 테스트에서 해당 객체를 조롱하고 싶지는 않습니다."
Cygon

72

싱글 톤은 하나의 클래스에서 두 가지 나쁜 특성을 결합하는 기능을 제공합니다. 그것은 거의 모든면에서 잘못되었습니다.

싱글 톤이 제공합니다 :

  1. 객체에 대한 글로벌 액세스
  2. 이 유형의 개체를 두 개 이상 만들 수 없다는 보장

첫 번째는 간단합니다. 세계는 일반적으로 나쁘다. 실제로 필요한 경우가 아니면 전역 적으로 액세스 가능한 객체를 만들면 안됩니다 .

두 번째는 말이되는 것처럼 들리 겠지만 생각해 보자. 기존 객체를 참조하는 대신 마지막으로 ** 실수로 * 새로운 객체를 만든 시간은 언제입니까? C ++로 태그가 지정되었으므로 해당 언어의 예제를 사용하십시오. 실수로 글을 쓰십니까

std::ostream os;
os << "hello world\n";

쓰려고 할 때

std::cout << "hello world\n";

당연히 아니지. 이런 종류의 오류는 발생하지 않기 때문에이 오류에 대한 보호가 필요하지 않습니다. 그렇다면 올바른 반응은 집에 가서 12-20 시간 동안 자고 기분이 나아지기를 바랍니다.

객체가 하나만 필요한 경우 인스턴스를 하나만 만들면됩니다. 하나의 객체가 전역 적으로 액세스 가능해야하는 경우 전역으로 만드십시오. 그렇다고 다른 인스턴스를 만드는 것이 불가능하다는 의미는 아닙니다.

"단일 인스턴스 만 가능"제약 조건은 실제로 버그 가능성으로부터 우리를 보호하지 않습니다. 그러나 않습니다 리팩토링 열심히 우리의 코드를 확인하고 유지합니다. 나중에 자주 두 개 이상의 인스턴스가 필요하다는 것을 알게 되었기 때문에 . 우리는 어떻게 우리가 하나 이상의 데이터베이스가 않습니다 , 우리는 여러 로거를 하나 이상 구성 개체를 원하는가했다. 우리의 단위 테스트는 일반적인 테스트를 위해 매 테스트마다 이러한 객체를 생성하고 다시 만들 수 있기를 원할 수 있습니다.

싱글은 경우에만 사용해야합니다 그래서, 우리는 필요 모두에게 그것을 제공하는 특성을 우리가 경우에 필요한 글로벌 액세스 (전역은 일반적으로 낙심하고 있기 때문에, 희소 인) 우리가 필요로 하는 사람을 방지하기 위해 (A)의 하나 개 이상의 인스턴스를 생성 클래스 (디자인 문제처럼 들립니다). 내가 볼 수있는 유일한 이유는 두 개의 인스턴스를 만들면 응용 프로그램 상태가 손상 될 수 있기 때문입니다. 어떤 경우에 그 대답은 그 클래스를 고치는 것입니다. 유일한 인스턴스에 의존해서는 안됩니다.

객체에 대한 전역 액세스가 필요한 경우와 같이 전역으로 만듭니다 std::cout. 그러나 생성 할 수있는 인스턴스 수를 제한하지 마십시오.

절대적으로 클래스의 인스턴스 수를 하나만으로 제한해야하며 두 번째 인스턴스 생성을 안전하게 처리 할 수있는 방법이 없다면 시행하십시오. 그러나 전 세계적으로 접근 가능하도록 만들지 마십시오.

두 가지 특성이 모두 필요하다면 1) 싱글 톤으로 만들고 2) 그런 경우를 상상하기가 어렵 기 때문에 필요한 것을 알려주십시오.


3
또는 글로벌로 만들 수 있고 싱글 톤의 단점 중 하나만 얻을 수 있습니다. 싱글 톤을 사용하면 동시에 해당 데이터베이스 클래스의 한 인스턴스로 자신을 제한 할 수 있습니다. 왜 그럴까요? 또는 왜 인스턴스화 목록이 "실제로 길어질"정도로 많은 종속성이 있는지 살펴볼 수 있습니다. 모두 필요합니까? 이들 중 일부를 다른 구성 요소에 위임해야합니까? 아마도 그들 중 일부는 구조체로 함께 패키지되어 단일 인수로 전달할 수 있습니다. 많은 솔루션이 있습니다. 모두 싱글 톤보다 낫습니다.
jalf

6
그렇습니다, 거기에 싱글 톤 정당화 될 수 있습니다. 그러나 나는 당신이 단지 매우 이국적인 경우에만 필요하다는 내 요지를 입증했다고 생각합니다. 대부분의 소프트웨어는 제설기 하드웨어를 다루지 않습니다. 그러나 나는 여전히 확신하지 못한다. 실제 응용 프로그램에서이 중 하나만 원한다는 것에 동의합니다. 그러나 단위 테스트는 어떻습니까? 각각 독립적으로 실행해야하므로 단일 톤으로는 처리하기 어려운 자체 SpreaderController를 작성하는 것이 이상적입니다. 마지막으로 동료가 처음에 여러 인스턴스를 만드는 이유는 무엇입니까? 이것이 현실적인 시나리오일까요?
jalf

3
마지막 두 예제는 "단일 인스턴스"제한을 정당화 할 수 있지만 "전 세계적으로 액세스 가능한"제한을 정당화 할 수는 없습니다. 지구상에서 전체 코드베이스가 전화 스위치의 관리 장치에 액세스 할 수있는 이유는 무엇입니까? 싱글 톤의 요점은 두 가지 특성 을 모두 제공하는 것 입니다. 둘 중 하나만 필요하면 싱글 톤을 사용하지 않아야합니다.
jalf

2
@ jalf-내 목표는 Singleton이 어디에서나 유용 할 수 있는지에 대한 예제를 제공하는 것이 었습니다. 현재 작업 라인에 적용하기 위해 여러 번 보지 못하는 것 같습니다. Singleton을 사용할 수 있기 때문에 비즈니스 응용 프로그램에서만 제설기 프로그래밍으로 전환했습니다. :) j / k 나는 이런 일을하는 더 좋은 방법이 있다는 당신의 전제에 동의합니다. 토론 주셔서 감사합니다!
J. Polfer

2
싱글 톤 ( AHEM! ) "패턴"을 사용하여 사람들이 더 많은 인스턴스를 인스턴스화하는 것을 방지하는 것은 사람들이 악의적으로 그렇게하지 못하도록 막는 오래된 멍청한 짓입니다. 내 작은 함수에 Foo 유형의 로컬 변수 foo1이 있고 함수에서 하나만 원할 때 누군가가 두 번째 Foo 가변 foo2를 만들어 원래의 변수 대신 사용할 것이라고 걱정하지 않습니다.
Thomas Eding

36

싱글 톤의 문제점은 구현이 아닙니다. 그것들은 두 가지 다른 개념을 혼동한다는 것입니다.

1) 싱글 톤은 객체에 대한 전역 액세스 메커니즘을 제공합니다. 비록 잘 정의 된 초기화 순서가없는 언어에서는 그것들이 스레드로부터 안전하거나 약간 더 안정적 일 수 있지만,이 사용법은 여전히 ​​전역 변수와 도덕적으로 동일합니다. 이 변수는 어색한 구문 (g_foo 대신 foo :: get_instance ())으로 작성된 전역 변수이지만 동일한 목적 (전체 프로그램에서 액세스 가능한 단일 객체)을 제공하며 동일한 단점을 가지고 있습니다.

2) 싱글 톤은 클래스의 다중 인스턴스화를 방지합니다. 이런 종류의 기능을 클래스로 구워야하는 경우는 거의 없습니다. 일반적으로 상황이 훨씬 더 중요합니다. 일대일로 간주되는 많은 것들이 실제로는 일대일 일뿐입니다. IMO보다 적합한 솔루션은 인스턴스가 하나 이상 필요하다는 사실을 알 때까지 인스턴스를 하나만 만드는 것입니다.


6
동의했다. 현실 세계에서 일부에 따르면 두 가지 잘못이 옳을 수 있습니다. 그러나 프로그래밍에서 두 가지 나쁜 아이디어를 혼합해도 좋은 아이디어는 아닙니다.
jalf

27

패턴이있는 한 가지 : 일반화하지 마십시오 . 그들은 유용하고 실패 할 때 모든 경우를 가지고 있습니다.

코드 를 테스트 해야 할 때 싱글 톤이 불쾌 할 수 있습니다 . 일반적으로 클래스의 인스턴스 하나에 붙어 있으며 생성자에서 문을 열거 나 상태를 재설정하는 방법 등을 선택할 수 있습니다.

다른 문제는 실제로 싱글 톤 은 변장에 있는 전역 변수 에 지나지 않는다는 것 입니다. 프로그램에 비해 전역 공유 상태가 너무 많으면 상황이 거꾸로 돌아가는 경향이 있습니다. 모두 알고 있습니다.

종속성 추적이 더 어려워 질 수 있습니다 . 모든 것이 싱글 톤에 의존 할 때, 변경하기가 더 어렵고, 둘로 나뉘 기 어렵습니다. 이것은 또한 유연성을 방해합니다. 이 문제를 완화하기 위해 일부 Dependency Injection 프레임 워크를 조사하십시오 .


8
아니요, 싱글 톤은 변장의 전역 변수보다 훨씬 더 많습니다. 그것이 특히 나쁜 이유입니다. 그것은 또 다른 개념 (보통 나쁜) 글로벌 다움 결합 (그가 인스턴스를 필요로 결정하면 클래스의 인스턴스 프로그래머를 허용하지 않습니다 것을) 그들은 자주하는 잘못된 사용 예, 글로벌 변수로합니다. 그런 다음 다른 불쾌한 부작용을 끌어와 코드베이스를 무너 뜨립니다.
jalf

7
또한 싱글 톤에는 공개 액세스 기능이 필요하지 않습니다. 싱글 톤은 라이브러리 내부에있을 수 있으며 사용자에게 노출되지 않습니다. 따라서 반드시 그런 의미에서 "글로벌"인 것은 아닙니다.
Steven Evers

1
싱글 톤이 테스트하는 데 얼마나 많은 고통을 주는지 +1
DevSolar

1
@jalf 누군가가 하나 이상의 클래스 인스턴스를 만들도록 허용하지 않는 것은 나쁜 일이 아닙니다. 실제로 인스턴스화 된 클래스의 인스턴스가 하나만 있어야하는 경우 요구 사항이 적용됩니다. 나중에 다른 인스턴스를 만들어야한다고 결정한 경우 처음에는 싱글 톤이 아니 었으므로 리팩터링해야합니다.
William

2
@ William : 그리고 때때로 여러 로거가 있어야했습니다. 싱글 톤을 주장하는 것이 아니라 평범한 오래된 지역을 주장하고 있습니다. 당신은 알고 싶어요 로거는 항상 사용할 수 있습니다. 이것이 전 세계를위한 것입니다. 다른 로거가 인스턴스화 될 수 없다는 것을 알 필요가 없습니다 . 이것이 싱글 톤이 시행하는 것입니다. (로거에 대한 단위 테스트 작성을 시도하십시오-필요에 따라 작성 및 파괴 할 수 있으면 훨씬 쉬우 며 싱글 톤으로는 불가능합니다)
jalf

13

싱글 톤은 기본적으로 복잡한 전역 변수를 갖는 것이 어렵거나 불가능한 언어로 복잡한 전역 상태를 가질 수 있습니다.

Java는 특히 단일 변수를 전역 변수의 대체물로 사용합니다. 모든 것이 클래스 내에 포함되어야하기 때문입니다. 전역 변수에 가장 근접한 것은 공용 정적 변수이며 전역 변수처럼 사용할 수 있습니다import static

C ++에는 전역 변수가 있지만 전역 클래스 변수의 생성자가 호출되는 순서는 정의되어 있지 않습니다. 따라서 싱글 톤을 사용하면 변수가 처음 필요할 때까지 전역 변수 작성을 연기 할 수 있습니다.

파이썬과 루비와 같은 언어는 단일 톤을 거의 사용하지 않습니다. 모듈 내에서 전역 변수를 대신 사용할 수 있기 때문입니다.

그렇다면 싱글 톤을 사용하는 것이 언제 / 좋은가? 전역 변수를 사용하는 것이 좋거나 나쁠 때와 거의 같습니다.


전역 변수가 "좋은"때는 언제입니까? 때로는 문제에 대한 최상의 해결 방법이지만 결코 "좋은"것은 아닙니다.
DevSolar

1
전역 변수는 모든 곳에서 사용될 때 좋고 모든 것이 액세스 할 수 있습니다. 단일 상태 터링 머신의 구현은 단일 톤을 사용할 수 있습니다.
리 루비 에르

나는 "글로벌을 사용하는 것이 좋거나 나쁠 때"라는 대답에서 간접적 인 계층을 좋아합니다. DevSolar와 Lee Louviere는 응답 시간에 누가 주석을 달 것인지 알 수 없었지만 동의하는 가치를 얻습니다.
Praxeolitic

6

Alexandrescu의 최신 C ++ 디자인 에는 스레드 안전하고 상속 가능한 일반 싱글 톤이 있습니다.

내 2p-worth의 경우 싱글 톤의 수명을 정의하는 것이 중요하다고 생각합니다 (절대로 사용해야 할 때). 나는 보통 정적 get()함수가 아무것도 인스턴스화 하지 못하게 하고 기본 응용 프로그램의 일부 전용 섹션에 설정 및 파괴를 남겨 둡니다. 이는 싱글 톤 간의 종속성을 강조하는 데 도움이되지만 위에서 강조한 것처럼 가능한 경우 피하는 것이 가장 좋습니다.


6
  • 싱글 톤을 올바르게 구현하는 방법

한 번도 언급 한 적이없는 한 가지 문제가 있습니다. 이전 작업에서 발생한 문제입니다. 우리는 DLL간에 공유되는 C ++ 싱글 톤을 가지고 있었고 클래스의 단일 인스턴스가 작동하지 않도록하는 일반적인 메커니즘을 가지고 있습니다. 문제는 각 DLL이 EXE와 함께 자체 정적 변수 세트를 얻는다는 것입니다. get_instance 함수가 인라인 또는 정적 라이브러리의 일부인 경우, 각 DLL은 "단일"의 자체 사본으로 구성됩니다.

해결 방법은 싱글 톤 코드가 하나의 DLL 또는 EXE에만 정의되어 있는지 확인하거나 해당 속성을 가진 싱글 톤 관리자를 만들어 인스턴스를 소포하는 것입니다.


10
Yo dawg, 나는 싱글 톤을 좋아한다고 들었으므로 싱글 톤을 위해 싱글 톤을 만들었으므로 안티 패턴 동안 안티 패턴을 만들 수 있습니다.
Eva

@ 에바, 그래 그런 것. 나는 문제를 만들지 않고 어떻게 든 작동하게해야했습니다.
마크 랜섬

5

첫 번째 예제는 스레드 안전하지 않습니다. 두 개의 스레드가 동시에 getInstance를 호출하면 해당 정적은 PITA가됩니다. 어떤 형태의 뮤텍스가 도움이 될 것입니다.


그러나 위의 의견에 언급되어 있습니다. * 제한 사항 : 단일 스레드 설계 * 참조 : aristeia.com/Papers/DDJ_Jul_Aug_2004_revised.pdf * 다중 스레드 응용 프로그램의 잠금과 관련된 문제
Martin York

정적 메소드로 getInstance 만 사용하고 다른 오퍼레이션에 대한 인스턴스 메소드를 사용하는 클래식 싱글 톤은 절대 스레드로부터 안전하지 않습니다. (쓰레드 로컬 스토리지를 사용하여 스레드 당 싱글 톤으로 만들지 않는 한 ...)
Tobi

c ++ 11 이상에서도?
hg_git

5

다른 사람들이 지적했듯이 싱글 톤의 주요 단점은 확장 할 수 없으며 테스트 목적으로 둘 이상의 인스턴스를 인스턴스화 할 수있는 힘을 잃는 것입니다.

싱글 톤의 유용한 측면 :

  1. 게 으르거나 선행 인스턴스화
  2. 설정 및 / 또는 상태가 필요한 객체에 편리

그러나 이러한 이점을 얻기 위해 싱글 톤을 사용할 필요는 없습니다. 작업을 수행하는 일반 객체를 작성한 다음 사람들이 공장 (별도의 객체)을 통해 액세스하도록 할 수 있습니다. 공장은 필요할 경우 인스턴스화 및 재사용 등에 대해서만 걱정할 수 있습니다. 또한 구체적인 클래스가 아닌 인터페이스로 프로그래밍하면 팩토리에서 전략을 사용할 수 있습니다. 즉, 인터페이스의 다양한 구현을 켜고 끌 수 있습니다.

마지막으로, 공장은 Spring과 같은 의존성 주입 기술에 적합합니다.


3

싱글 톤은 초기화하고 객체화 할 때 많은 코드가 실행될 때 편리합니다. 예를 들어, 영속 객체를 설정할 때 iBatis를 사용하는 경우 코드에 도달하기 전에 모든 구성을 읽고 맵을 구문 분석하고 모두 올바른지 확인해야합니다.

매번이 작업을 수행하면 성능이 크게 저하됩니다. 싱글 톤으로 사용하면 한 번 적중 한 다음 모든 후속 통화에서 수행 할 필요가 없습니다.


프로토 타입 패턴은 물론이 작업을 수행하고, 더 유연. 클라이언트가 비싼 클래스의 인스턴스를 많이 만들 때도 사용할 수 있지만 제한된 수의 인스턴스 만 실제로 다른 상태를 갖습니다. 예를 들어 Tetris의 tetronimos입니다.
Eva

3

싱글 톤의 진정한 몰락은 상속을 깰 수 있다는 것입니다. Singleton이 참조되는 코드에 액세스 할 수 없으면 확장 된 기능을 제공하기 위해 새 클래스를 파생시킬 수 없습니다. 따라서 Singleton은 코드를 단단히 결합하여 (전략 패턴 ... 일명 종속성 주입으로 수정 가능) 코드 섹션을 수정 (공유 라이브러리)에서 종료하지 못하게합니다.

따라서 로거 또는 스레드 풀의 예조차 유효하지 않으므로 전략으로 대체해야합니다.


로거 자체는 싱글 톤이 아니어야합니다. 일반적인 "브로드 캐스트"메시징 시스템이어야합니다. 로거 자체는 브로드 캐스트 메시지의 가입자입니다.
CashCow

스레드 풀도 싱글 톤이 아니어야합니다. 일반적인 질문은 둘 이상을 원하십니까? 예. 마지막으로 사용했을 때 하나의 응용 프로그램에 3 개의 다른 스레드 풀이있었습니다.
CashCow

3

대부분의 사람들은 전역 변수 사용에 대해 기분을 좋게 만들려고 할 때 싱글 톤을 사용합니다. 합법적 인 용도가 있지만 사람들이 사용하는 대부분의 경우 인스턴스가 하나만 존재할 수 있다는 사실은 전 세계적으로 액세스 할 수 있다는 사실과 비교할 때 사소한 사실입니다.


3

싱글 톤은 하나의 인스턴스 만 생성 할 수 있으므로 인스턴스 복제를 효과적으로 제어합니다. 예를 들어 조회의 여러 인스턴스가 필요하지 않습니다. 예를 들어 모스 조회 맵은 단일 클래스로 래핑하는 것이 적합합니다. 그리고 클래스의 단일 인스턴스가 있다고해서 해당 인스턴스에 대한 참조 수가 제한되어있는 것은 아닙니다. 스레딩 문제를 피하기 위해 호출을 인스턴스에 대기시키고 필요한 변경 사항을 적용 할 수 있습니다. 그렇습니다. 싱글 톤의 일반적인 형태는 전 세계적으로 공개 된 형태이므로 액세스 제한 싱글 톤을 만들기 위해 디자인을 확실히 수정할 수 있습니다. 나는 전에 이것을 피곤하지 않았지만 그것이 가능하다는 것을 알고 있습니다. 그리고 싱글 톤 패턴이 완전히 악하다고 말하는 모든 사람들에게 당신은 이것을 알아야합니다 :



2

다음은 소멸자 자체에서 메모리를 할당 해제하여 스레드 안전 싱글 톤 패턴을 구현하는 더 나은 방법입니다. 그러나 프로그램이 종료되면 싱글 톤 인스턴스가 자동으로 파괴되므로 소멸자는 선택 사항이어야한다고 생각합니다.

#include<iostream>
#include<mutex>

using namespace std;
std::mutex mtx;

class MySingleton{
private:
    static MySingleton * singletonInstance;
    MySingleton();
    ~MySingleton();
public:
    static MySingleton* GetInstance();
    MySingleton(const MySingleton&) = delete;
    const MySingleton& operator=(const MySingleton&) = delete;
    MySingleton(MySingleton&& other) noexcept = delete;
    MySingleton& operator=(MySingleton&& other) noexcept = delete;
};

MySingleton* MySingleton::singletonInstance = nullptr;
MySingleton::MySingleton(){ };
MySingleton::~MySingleton(){
    delete singletonInstance;
};

MySingleton* MySingleton::GetInstance(){
    if (singletonInstance == NULL){
        std::lock_guard<std::mutex> lock(mtx);
        if (singletonInstance == NULL)
            singletonInstance = new MySingleton();
    }
    return singletonInstance;
}

싱글 톤 클래스를 사용해야하는 상황에 대해-프로그램 실행 전체에서 인스턴스 상태를 유지하려는 경우 파일의 한 인스턴스 만 필요한 애플리케이션의 실행 로그에 기록하는 경우 사용되는 등. 아무도 내 위의 코드에서 최적화를 제안 할 수 있다면 감사 할 것입니다.


2
그것은 확실히 나아지지 않습니다. 1 : 포인터를 사용하여 소유권 의미를 정의하지 않습니다. C ++에서는 포인터를 관리 할 준비가되어 있지 않으면 포인터를 사용해서는 안됩니다. 2 : 이중 체크 잠금을 사용하는 것은 구식이며이를 수행하는 훨씬 더 현대적인 방법이 있습니다. 3 : 파괴에 대한 당신의 의견은 순진합니다. 메모리 교정은 소멸자 사용 시점이 아니라 정리에 관한 것입니다. 더 나은 버전을위한 제안 : 질문을보십시오. 거기에 제시된 버전은 이미 훨씬 나아졌습니다.
Martin York

1

저는 싱글 톤을 인터뷰 테스트로 사용합니다.

개발자에게 일부 디자인 패턴의 이름을 물어볼 때 이름을 지정할 수있는 모든 것이 Singleton이면 고용되지 않습니다.


45
고용에 관한 단단하고 빠른 규칙은 다양한 잠재 직원을 놓칠 수 있습니다.
Karl

13
다양한 바보가 존재합니다. 그렇다고 그들이 고용을 고려해야한다는 의미는 아닙니다. 누군가가 디자인 패턴을 전혀 언급 할 수 없다면 싱글 톤을 알고 다른 패턴을 모르는 사람보다 선호한다고 생각합니다.
jalf

3
기록적인 책-나의 대답은 혀에 뺨이었다. 실제 인터뷰 과정에서 C ++로 누군가를지도해야하는지, 그리고 얼마나 힘든지 평가하려고합니다. 내가 가장 좋아하는 후보 중 일부는 C ++을 모르는 사람들이지만 그에 대해 큰 대화를 나눌 수있었습니다.
Matt Cruikshank

4
다운 투표. 내 개인적인 경험에서 프로그래머는 싱글 톤 이외의 다른 패턴의 이름을 지정할 수 없지만 싱글 톤을 사용한다는 의미는 아닙니다. 개인적으로, 나는 그것들을 들어보기 전에 내 코드에서 싱글 톤을 사용하고있었습니다 (나는 그들을 더 똑똑한 지구본이라고 불렀습니다-나는 세계가 무엇인지 알았습니다). 내가 그들에 대해 배웠을 때, 그들의 장단점에 대해 배웠을 때-나는 그 사용을 중단했습니다. 갑자기 단위 테스트가 더 흥미로워졌습니다. 그로 인해 프로그래머가 더 나빠졌습니까? Pfff ...
Paulius 2018 년

3
나는 또한 "일부 디자인 패턴 이름"에 대한 넌센스 질문에 대해 하향 투표하고 있습니다. 디자인은 단순히 이름을 릴 수있는 것이 아니라 디자인 패턴을 적용하는 방법을 이해하는 것입니다. 좋아, 그것은 downvote를 보증하지는 않지만이 대답은 트롤입니다.
CashCow

0

사용 방지 :

과도한 싱글 톤 사용의 한 가지 주요 문제점은 패턴이 대체 구현을 쉽게 확장하고 교체 할 수 없다는 것입니다. 클래스 이름은 싱글 톤이 사용되는 곳마다 하드 코딩됩니다.


2 가지 이유로 하향 투표 : 1. 싱글 톤은 내부적으로 다형성 인스턴스를 사용할 수 있습니다 (예 : 전역 로거는 다형성 타겟팅 전략을 사용합니다) 2. 싱글 톤 이름에 대한 typedef가있을 수 있으므로 코드는 실제로 typedef에 의존합니다.
topright gamedev

나는 호기심이 반복되는 템플릿 패턴을 사용하여 확장 할 수 있도록 싱글 톤 버전을 만들었습니다.
Zachary Kraus

0

이것이 C # 에서 가장 강력한 버전 이라고 생각합니다 .

using System;
using System.Collections;
using System.Threading;

namespace DoFactory.GangOfFour.Singleton.RealWorld
{

  // MainApp test application

  class MainApp
  {
    static void Main()
    {
      LoadBalancer b1 = LoadBalancer.GetLoadBalancer();
      LoadBalancer b2 = LoadBalancer.GetLoadBalancer();
      LoadBalancer b3 = LoadBalancer.GetLoadBalancer();
      LoadBalancer b4 = LoadBalancer.GetLoadBalancer();

      // Same instance?
      if (b1 == b2 && b2 == b3 && b3 == b4)
      {
        Console.WriteLine("Same instance\n");
      }

      // All are the same instance -- use b1 arbitrarily
      // Load balance 15 server requests
      for (int i = 0; i < 15; i++)
      {
        Console.WriteLine(b1.Server);
      }

      // Wait for user
      Console.Read();    
    }
  }

  // "Singleton"

  class LoadBalancer
  {
    private static LoadBalancer instance;
    private ArrayList servers = new ArrayList();

    private Random random = new Random();

    // Lock synchronization object
    private static object syncLock = new object();

    // Constructor (protected)
    protected LoadBalancer()
    {
      // List of available servers
      servers.Add("ServerI");
      servers.Add("ServerII");
      servers.Add("ServerIII");
      servers.Add("ServerIV");
      servers.Add("ServerV");
    }

    public static LoadBalancer GetLoadBalancer()
    {
      // Support multithreaded applications through
      // 'Double checked locking' pattern which (once
      // the instance exists) avoids locking each
      // time the method is invoked
      if (instance == null)
      {
        lock (syncLock)
        {
          if (instance == null)
          {
            instance = new LoadBalancer();
          }
        }
      }

      return instance;
    }

    // Simple, but effective random load balancer

    public string Server
    {
      get
      {
        int r = random.Next(servers.Count);
        return servers[r].ToString();
      }
    }
  }
}

.NET 최적화 버전 은 다음과 같습니다 .

using System;
using System.Collections;

namespace DoFactory.GangOfFour.Singleton.NETOptimized
{

  // MainApp test application

  class MainApp
  {

    static void Main()
    {
      LoadBalancer b1 = LoadBalancer.GetLoadBalancer();
      LoadBalancer b2 = LoadBalancer.GetLoadBalancer();
      LoadBalancer b3 = LoadBalancer.GetLoadBalancer();
      LoadBalancer b4 = LoadBalancer.GetLoadBalancer();

      // Confirm these are the same instance
      if (b1 == b2 && b2 == b3 && b3 == b4)
      {
        Console.WriteLine("Same instance\n");
      }

      // All are the same instance -- use b1 arbitrarily
      // Load balance 15 requests for a server
      for (int i = 0; i < 15; i++)
      {
        Console.WriteLine(b1.Server);
      }

      // Wait for user
      Console.Read();    
    }
  }

  // Singleton

  sealed class LoadBalancer
  {
    // Static members are lazily initialized.
    // .NET guarantees thread safety for static initialization
    private static readonly LoadBalancer instance =
      new LoadBalancer();

    private ArrayList servers = new ArrayList();
    private Random random = new Random();

    // Note: constructor is private.
    private LoadBalancer()
    {
      // List of available servers
      servers.Add("ServerI");
      servers.Add("ServerII");
      servers.Add("ServerIII");
      servers.Add("ServerIV");
      servers.Add("ServerV");
    }

    public static LoadBalancer GetLoadBalancer()
    {
      return instance;
    }

    // Simple, but effective load balancer
    public string Server
    {
      get
      {
        int r = random.Next(servers.Count);
        return servers[r].ToString();
      }
    }
  }
}

이 패턴은 dotfactory.com 에서 찾을 수 있습니다 .


3
코드를보다 쉽게 ​​읽을 수 있도록 싱글 톤에 특별히 관련이없는 부분을 제거 할 수 있습니다.
Martin York

또한 첫 번째 버전은 읽기 / 쓰기 순서 변경이 가능하므로 스레드로부터 안전하지 않습니다. 참조 stackoverflow.com/questions/9666/...
토마스 Danecker

5
어 .. 잘못된 언어? 이 질문에는 분명히 C ++ 태그가 붙어 있습니다.
DevSolar

0

Meyers 싱글 톤 패턴은 대부분의 시간 동안 충분히 잘 작동하며 경우에 따라 더 나은 것을 찾기 위해 돈을 지불 할 필요는 없습니다. 생성자가 절대로 던지지 않고 싱글 톤 사이에 종속성이없는 한.

싱글 톤은 모든 GAO가 싱글 톤 인 것은 아니지만 전역 적으로 액세스 가능한 객체 (지금부터 GAO )에 대한 구현입니다 .

로거 자체는 싱글 톤이 아니어야하지만 로그 방법은 로그 메시지가 생성되는 위치 또는 방법에서 로그 메시지가 생성되는 위치를 분리하기 위해 이상적으로 전역 적으로 액세스 할 수 있어야합니다.

게으른 로딩 / 게으른 평가는 다른 개념이며 싱글 톤도 일반적으로 구현합니다. 그것은 자체의 많은 문제, 특히 스레드 안전성과 예외가 발생했을 때 문제가 발생했을 때 좋은 아이디어처럼 보이는 것이 결국에는 그리 좋지 않은 것으로 나타났습니다. (문자열의 COW 구현과 약간 비슷 함).

이를 염두에두고 GOA를 다음과 같이 초기화 할 수 있습니다.

namespace {

T1 * pt1 = NULL;
T2 * pt2 = NULL;
T3 * pt3 = NULL;
T4 * pt4 = NULL;

}

int main( int argc, char* argv[])
{
   T1 t1(args1);
   T2 t2(args2);
   T3 t3(args3);
   T4 t4(args4);

   pt1 = &t1;
   pt2 = &t2;
   pt3 = &t3;
   pt4 = &t4;

   dostuff();

}

T1& getT1()
{
   return *pt1;
}

T2& getT2()
{
   return *pt2;
}

T3& getT3()
{
  return *pt3;
}

T4& getT4()
{
  return *pt4;
}

그렇게 조잡하게 수행 할 필요는 없으며 객체를 포함하는로드 된 라이브러리에서 명확하게 수명을 관리하는 다른 메커니즘을 원할 것입니다. (라이브러리를로드 할 때 얻을 수있는 객체에 넣으십시오).

싱글 톤을 사용하는 경우는? dlopen으로로드 된 라이브러리를 나타내는 싱글 톤 테이블-로거가 가입 할 수 있고 메시지를 보낼 수있는 메시지 핸들러입니다. 신호 처리기에 특별히 필요합니다.


0

나는 왜 싱글 톤이 세계적이어야 하는지를 알지 못한다.

클래스 내부의 데이터베이스를 개인 상수 정적 변수로 숨기고 데이터베이스를 사용자에게 노출시키지 않고 데이터베이스를 활용하는 클래스 함수를 만드는 단일 톤을 생성하려고했습니다.

이 기능이 왜 나쁜지 모르겠습니다.


왜 그것이 세계적이어야한다고 생각하는지 모르겠습니다.
Martin York

이 글에 따르면, 모든 사람들은 싱글 톤이 세계적이어야한다고 말합니다
Zachary Kraus

1
스레드는 싱글 톤이 전역 상태임을 나타냅니다. 전역 변수가 아닙니다. 제안한 솔루션은 글로벌 상태입니다. 제안한 솔루션은 전역 변수를 사용하고 있습니다. 클래스의 정적 멤버는 "정적 저장 기간"의 개체이고 전역 변수는 "정적 저장 기간"의 개체입니다. 따라서 두 가지 의미와 범위가 약간 다른 기본적으로 동일합니다.
Martin York

"정적 저장 기간"으로 인해 개인 정적 변수가 여전히 전역 변수입니까?
Zachary Kraus

1
참고 : 당신은 내 의도적으로 언급 한 비트를보고 싶었습니다. 정적 "비공개"멤버를 사용하는 디자인은 싱글 톤과 같은 방식으로 나쁘지 않습니다. "글로벌 변경 가능 상태"를 도입하지 않기 때문입니다. 그러나 싱글 톤도 아닙니다. 싱글 톤은 객체의 인스턴스가 하나만 존재할 수 있도록 설계된 클래스입니다. 당신이 제안하는 것은 클래스의 모든 객체에 대한 단일 공유 상태입니다. 다른 개념.
Martin York

0

많은 메모리를 캡슐화하는 클래스가있을 때 유용합니다. 예를 들어 최근에 작업 한 게임에서 매우 큰 연속 메모리 컬렉션을 포함하는 영향 맵 클래스가 있습니다. 시작시 할당 된 모든 것을 종료하고 종료시 모두 해제하기를 원하며 사본 하나만 원합니다. 또한 여러 곳에서 액세스해야합니다. 이 경우 싱글 톤 패턴이 매우 유용하다는 것을 알았습니다.

다른 솔루션이 있다고 확신하지만이 솔루션은 매우 유용하고 구현하기 쉽습니다.


0

당신이 싱글 톤을 만든 사람이고 그것을 사용하는 사람이라면, 그것을 싱글 톤으로 만들지 마십시오 (단일 톤을 만들지 않고 객체의 특이성을 제어 할 수 있기 때문에 의미가 없습니다). 라이브러리를 사용하여 사용자에게 하나의 개체 만 제공하려고합니다 (이 경우 단일 항목을 만든 사람이지만 사용자는 아닙니다).

싱글 톤은 객체이므로 객체로 사용합니다. 많은 사람들이 단일 메서드를 반환하는 메소드를 호출하여 직접 싱글 톤에 액세스하지만 코드에서 객체가 싱글 톤임을 알기 때문에 해 롭습니다. 나는 싱글 톤을 객체로 사용하는 것을 선호합니다. 생성자를 통해 일반 객체로 사용합니다. 그러면 코드에서 이러한 객체가 싱글 톤인지 아닌지 알지 못하므로 종속성이 더 명확 해지 며 리팩토링에 약간 도움이됩니다 ...


-1

데스크톱 응용 프로그램 (우리는 공룡 만 더 이상 이것을 쓰고 있습니다!)은 상대적으로 변경되지 않는 전역 응용 프로그램 설정 (사용자 언어, 파일 경로, 사용자 기본 설정 등)을 얻는 데 필수적입니다. 그렇지 않으면 모든 클래스와 모든 대화 상자에 전파되어야합니다 .

편집-물론 이것들은 읽기 전용이어야합니다!


그러나 이것은 질문을 구걸하고있다. 왜 도움말 파일에 대한 사용자의 언어와 경로는 인스턴스 방법으로해야합니까 전혀 ?
DrPizza

2
우리는 그것을위한 세계를 가지고 있습니다. 싱글 톤으로 만들 필요가 없습니다
jalf

전역 변수-레지스트리 / 데이터베이스에서 어떻게 직렬화합니까? Gobal 수업-그렇다면 그들 중 하나만 어떻게 보장합니까?
Martin Beckett

@mgb : 레지스트리 / 데이터베이스에서 값을 읽고 전역 변수에 저장하여 직렬화합니다 (주 함수의 맨 위에서 수행해야 함). 클래스의 객체를 하나만 생성하여 클래스의 객체가 하나만 있는지 확인하십시오 ... 실제로 ... 'grep -rn "new \ + global_class_name"입니다. ? 정말?
Paulius

7
@ mgb : 지구상에서 왜 하나만 있는지 확인해야합니까? 하나의 인스턴스가 항상 현재 설정을 나타냅니다 . 그러나 다른 설정 개체를 장소 주변에 두지 않아야하는 이유는 없습니다. 예를 들어 "사용자가 현재 정의하고 있지만 아직 적용하지 않은 설정"에 대한 것일 수 있습니다. 또는 "사용자가 이전에 저장 한 구성을 나중에 다시 볼 수 있도록"구성한 것 또는 각 단위 테스트마다 하나씩.
jalf

-1

다른 구현

class Singleton
{
public:
    static Singleton& Instance()
    {
        // lazy initialize
        if (instance_ == NULL) instance_ = new Singleton();

        return *instance_;
    }

private:
    Singleton() {};

    static Singleton *instance_;
};

3
정말 끔찍하다. 더 나은 지연 초기화 및보다 중요한 결정 론적 파괴를 확인하려면 stackoverflow.com/questions/1008019/c-singleton-design-pattern/… 을 참조하십시오 .
Martin York

포인터를 사용하려는 경우 Instance()참조가 아닌 포인터를 반환해야합니다. .cpp파일 내 에서 인스턴스를 null로 초기화하십시오 Singleton* Singleton::instance_ = nullptr;. 그리고 다음과 Instance()같이 구현해야합니다 if (instance_ == nullptr) instance_ = new Singleton(); return instance_;.
Dennis
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.