[design-patterns] GOF 싱글 톤 패턴에 대한 실행 가능한 대안이 있습니까?

현실을 직시하자. 싱글 톤 패턴은 매우 논쟁 에 무리 프로그래머와 주제 모두 울타리의 측면. Singleton이 더 이상 영광스러운 전역 변수라고 느끼는 사람들과 패턴으로 맹세하고 그것을 끊임없이 사용하는 사람들이 있습니다. 그러나 나는 싱글 톤 논쟁 이 내 질문의 핵심에 놓여있는 것을 원하지 않습니다 . 누구나 줄다리기를하여 전투를 벌일 수 있으며 내가 관심을 갖는 모든 것을 누가이기는지 볼 수 있습니다. 제가 말하려는 것은 정답이 하나 있다고 믿지 않으며 의도적으로 당파 적 논쟁을 불러 일으키는 것도 아닙니다. 나는 질문을 할 때 단순히 단일 대안에 관심이 있습니다 .

GOF 싱글 톤 패턴에 대한 특정 대안이 있습니까?

예를 들어, 과거에 싱글 톤 패턴을 여러 번 사용했을 때 저는 단순히 하나 또는 여러 변수의 상태 / 값을 보존하는 데 관심이 있습니다. 그러나 변수의 상태 / 값은 단일 패턴을 사용하는 대신 정적 변수 를 사용하여 클래스의 각 인스턴스화 사이에 보존 될 수 있습니다 .

다른 아이디어가 있습니까?

편집 : “싱글 톤을 올바르게 사용하는 방법”에 대한 또 다른 게시물이되기를 원하지 않습니다. 다시 말하지만 나는 그것을 피할 방법을 찾고 있습니다. 재미로 알았지? 나는 당신의 최고의 영화 예고편 목소리로 순수하게 학문적 인 질문을하고있는 것 같습니다. “싱글 톤이없는 평행 우주에서 우리는 무엇을 할 수 있습니까?”



답변

Patterns I Hate “의 Alex Miller 는 다음을 인용합니다.

“싱글 톤이 답처럼 보일 때 다음과 같은 것이 더 현명한 경우가 많습니다.

  1. 인터페이스 및 싱글 톤의 기본 구현 생성
  2. 시스템의 “상단”에 기본 구현의 단일 인스턴스를 생성합니다. 이것은 Spring 구성 또는 코드에 있거나 시스템에 따라 다양한 방식으로 정의 될 수 있습니다.
  3. 필요한 각 구성 요소에 단일 인스턴스 전달 (종속성 주입)


답변

Singleton을 해결하는 적절한 방법을 이해하려면 Singleton (및 일반적으로 전역 상태)의 문제점을 이해해야합니다.

싱글 톤은 종속성을 숨 깁니다.

그게 왜 중요할까요?

종속성을 숨기면 커플 링 양을 추적하지 못하는 경향이 있기 때문입니다.

당신은

void purchaseLaptop(String creditCardNumber, int price){
  CreditCardProcessor.getInstance().debit(creditCardNumber, amount);
  Cart.getInstance().addLaptop();
}

보다 간단합니다

void purchaseLaptop(CreditCardProcessor creditCardProcessor, Cart cart,
                    String creditCardNumber, int price){
  creditCardProcessor.debit(creditCardNumber, amount);
  cart.addLaptop();
}

하지만 적어도 두 번째 API는 메소드의 공동 작업자가 무엇인지 정확히 알려줍니다.

따라서 Singleton을 해결하는 방법은 정적 변수 또는 서비스 로케이터를 사용하는 것이 아니라 Singleton 클래스를 인스턴스로 변경하는 것입니다. IoC 프레임 워크를 사용하여이를 처리하거나 수동으로 수행 할 수 있지만 중요한 것은 전역 상태를 제거하고 종속성 및 공동 작업을 명시 적으로 만드는 것입니다.


답변

내가 찾은 최고의 솔루션은 팩토리 패턴을 사용하여 클래스의 인스턴스를 구성하는 것입니다. 패턴을 사용하여 다음을 수행 할 수 있습니다 확신 을 사용하는 객체간에 공유되는 클래스의 하나의 인스턴스 만이 있음.

관리하기가 복잡 하겠지만이 블로그 게시물 “모든 싱글 톤이 사라진 곳은 어디입니까?” , 너무 자연스러워 보입니다. 그 외에도 단위 테스트를 분리하는 데 많은 도움이됩니다.

요약하면 무엇을해야합니까? 객체가 다른 객체에 종속 될 때마다 생성자를 통해서만 인스턴스를받습니다 (클래스에 새 키워드 없음).

class NeedyClass {

    private ExSingletonClass exSingleton;

    public NeedyClass(ExSingletonClass exSingleton){
        this.exSingleton = exSingleton;
    }

    // Here goes some code that uses the exSingleton object
}

그리고 공장.

class FactoryOfNeedy {

    private ExSingletonClass exSingleton;

    public FactoryOfNeedy() {
        this.exSingleton = new ExSingletonClass();
    }

    public NeedyClass buildNeedy() {
        return new NeedyClass(this.exSingleton);
    }
}

팩토리를 한 번만 인스턴스화하므로 exSingleton의 단일 인스턴스화가 있습니다. buildNeedy를 호출 할 때마다 NeedyClass의 새 인스턴스가 exSingleton과 함께 번들로 제공됩니다.

이게 도움이 되길 바란다. 실수가 있으면 지적 해주세요.


답변

Spring이나 다른 IoC-Container는 그 점에서 상당히 좋은 일을합니다. 클래스는 앱 자체 외부에서 생성 및 관리되기 때문에 컨테이너는 간단한 클래스 싱글 톤을 만들고 필요한 곳에 삽입 할 수 있습니다.


답변

패턴을 피하기 위해 길을 잃지 않아도됩니다. 패턴의 사용은 디자인 결정이거나 자연스러운 적합성입니다 (단지 제자리에 있음). 시스템을 설계 할 때 패턴을 사용하거나 사용하지 않도록 선택할 수 있습니다. 그러나 궁극적으로 디자인 선택이되는 것을 피하려고해서는 안됩니다.

저는 싱글 톤 패턴을 피하지 않습니다. 적절하고 내가 사용하거나 적절하지 않고 사용하지 않습니다. 그렇게 간단하다고 생각합니다.

Singleton의 적절성 (또는 부족)은 상황에 따라 다릅니다. 설계 결정을 내려야하며 그 결정의 결과를 이해하고 문서화해야합니다.


답변

Monostate (Robert C. Martin의 Agile Software Development에 설명 됨)는 singleton의 대안입니다. 이 패턴에서 클래스의 데이터는 모두 정적이지만 getter / setter는 정적이 아닙니다.

예를 들면 :

public class MonoStateExample
{
    private static int x;

    public int getX()
    {
        return x;
    }

    public void setX(int xVal)
    {
        x = xVal;
    }
}

public class MonoDriver
{
    public static void main(String args[])
    {
        MonoStateExample m1 = new MonoStateExample();
        m1.setX(10);

        MonoStateExample m2 = new MonoStateExample();
        if(m1.getX() == m2.getX())
        {
            //singleton behavior
        }
    }
}

Monostate는 singleton과 비슷한 동작을하지만 프로그래머가 singleton이 사용되고 있다는 사실을 반드시 인식하지 못하는 방식으로 작동합니다.


답변

싱글 때 상황이 있기 때문에 패턴이 존재 하나의 객체가 일련의 서비스를 제공하기 위해 필요하다 .

이 경우에도 인스턴스를 나타내는 전역 정적 필드 / 속성 을 사용하여 싱글 톤을 만드는 접근 방식을 여전히 고려하고 있습니다. 정적 필드와 개체가 제공하는 서비스가 아닌 개체 간의 코드에 종속성을 생성하기 때문에 부적절합니다.

따라서 고전적인 싱글 톤 패턴 대신 serviced container 와 함께 서비스 ‘like’패턴을 사용하는 것이 좋습니다 . 여기서 정적 필드를 통해 싱글 톤을 사용하는 대신 필요한 서비스 유형을 요청하는 메서드를 통해 참조를 얻습니다.

*pseudocode* currentContainer.GetServiceByObjectType(singletonType)
//Under the covers the object might be a singleton, but this is hidden to the consumer.

단일 글로벌 대신

*pseudocode* singletonType.Instance

이런 식으로 객체의 유형을 싱글 톤에서 다른 것으로 변경하고 싶을 때 쉽게 할 수 있습니다. 또한 추가적인 이점으로 모든 메서드에 할당 된 개체 인스턴스를 전달할 필요가 없습니다.

또한 Inversion of Control을 참조하십시오 . 아이디어는 단일 항목을 소비자에게 직접 노출함으로써 개체가 제공하는 개체 서비스가 아닌 소비자와 개체 인스턴스간에 종속성을 생성한다는 것입니다.

제 의견은 싱글 톤 패턴의 사용을 가능한 한 숨기는 것입니다. 왜냐하면 항상 피할 수 없거나 바람직하지는 않기 때문입니다.