2012-02-21 6 views
7

기존 프로젝트에서 저는 싱글 톤으로 선언 된 컨트롤러 클래스 (MVC)를 상속하여 내 자신의 정의를 정의합니다. 이 Singleton 클래스를 적절히 파생시키는 방법은 무엇입니까?C++ 싱글 톤 클래스 - 상속 우수 사례

먼저,이 상속에 대한 컨텍스트와 필요성이 확장됩니다.

기존 소프트웨어에 추가 된 응용 프로그램은 내가 수행하고자하는 것과 거의 동일한 작업을 수행하는 MVC 모듈을 사용하려고합니다. 서명과 약간의 수정까지 동일한 방법을 사용합니다. 내 자신의 MVC 모듈을 다시 작성하는 것은 확실히 코드 복제가 될 것입니다. 기존 모듈은 본질적으로 소프트웨어의 다른 부분에 적용되는 방향으로 지향되어 있으므로 동일한 모듈을 단순히 사용할 수는 없습니다. 그러나 Controller가 Singleton 인 Model-View-Controller 패턴으로 작성됩니다. 이미 View를 파생 시켰습니다.

둘째, 나는 클래식 클래스를 생성 할 수 있는지 의심 스럽습니다.

상속 된 클래스의 생성자를 호출하면 부모 클래스에 대해 getinstance()를 호출하고 파생 클래스 (?)에서 객체를 반환하지 않습니다.

셋째, 어떻게 대처할 수 있는지 보여줍니다. 의견을 보내주십시오/개선하는 데 도움이됩니다.

나는 AbstractController를 호출 할 수있는 클래스에서 전체 Singleton 클래스를 복사합니다. 나는이 클래스를 두 번 파생시킨다. 첫 번째 아이는 싱글 톤이며 부모 클래스의 전체 치료를 채택합니다. 두 번째 자식은 자신의 재정의 된 치료법을 사용하여 응용 프로그램의 일부분을 담당하는 컨트롤러입니다.

감사합니다.

+0

대부분의 경우, 클래스에 싱글 톤 기본 클래스가있는 경우에는 완전히 쓸모가 없습니다. 파생 클래스 인스턴스는 기본 클래스의 인스턴스이기 때문에 절대 만들 수 없습니다. 당신은 그 중 하나를 만들 수 없습니다. (나는 "합리적인"예외를 상상할 수 있지만 기본 클래스가 마음 속에 파생 된 아이디어로 특별히 설계된 경우에만 해당됩니다.) – Hurkyl

답변

3

당신이 완전히 다루고있는 상황을 이해하고 있는지, 그리고 싱글 톤에서 파생 될 수 있는지의 여부는 싱글 톤의 구현 방식에 달려 있습니다.

그러나 만약 당신이 "좋은 연습"을 언급 한 이후 질문을 읽을 때 마음에 와서 몇 가지 일반적인 점있다 :

  1. 상속은 일반적으로 코드 재사용을 달성하는 가장 좋은 도구가 아닙니다합니다. 참조 : Prefer composition over inheritance?

  2. 일반적으로 싱글 톤과 "좋은 연습"을 사용하면 함께 사용하지 마십시오! 참조 : 도움이 What is so bad about singletons?

희망을.

21

진실은, 싱글 톤 및 상속은 함께 잘 놀지 않는다.

네, 네, 싱글 애호가와 GoF의 숭배는 "당신이 당신의 생성자 보호 할 경우 잘 ..."와 "당신이 getInstance 방법을 가지고이없는 말,이 모든 저 이상이어야합니다 수업에, 당신은 그것을 넣을 수 있습니다 ... ",하지만 그들은 단지 내 요점을 증명하고 있습니다. 싱글 톤은 싱글 톤 (singleton)과베이스 클래스 (base class)가되기 위해 여러 가지 농구를 뛰어 넘어야합니다.

하지만 질문에 대답하기 위해 우리는 싱글 톤 기본 클래스가 있다고 가정 해보십시오. 상속을 통해 독신을 어느 정도 시행 할 수 있습니다. 생성자는 더 이상 private 일 수없는 몇 가지 작업 중 하나를 수행합니다. 다른 Base이 이미 있으면 예외를 throw합니다. 을 상속하는 클래스 Derived도 있다고 가정 해보십시오.상속을 허용 할 것이므로 에서 상속받을 수도 있고 그렇지 않을 수도있는 Base의 다른 하위 클래스가 여러 개있을 수 있다고 가정 해 보겠습니다.

하지만 문제가 있습니다. 바로 이미 실행 중이거나 곧 실행됩니다. 이미 객체를 생성하지 않고 Base::getInstance을 호출하면 null 포인터가 생성됩니다. 우리는 싱글 톤 객체가 존재하는 곳으로 돌아가고 싶습니다 (Base 및/또는 Derived 및/또는 Other 일 수 있음). 그러나 이렇게하는 것이 어렵고 여전히 모든 규칙을 따르십시오. 왜냐하면 그렇게 할 수있는 몇 가지 방법이 있기 때문입니다. 모든 방법에는 몇 가지 단점이 있습니다.

  • Base을 만들고 반환 할 수 있습니다. 나사 DerivedOther. 최종 결과 : Base::getInstance()은 항상 정확히 Base을 반환합니다. 하위 클래스는 결코 재생되지 않습니다. Kinda가 목적을 이겼습니다. IMO.

  • 우리는 파생 클래스에 getInstance을 넣을 수 있으며 특히 Derived을 원한다면 Derived::getInstance()이라고하는 호출자가있을 수 있습니다. 이렇게하면 호출자가 구체적으로 Derived을 요청해야한다는 사실을 알아야하기 때문에 커플 링이 크게 증가하고 결국 해당 구현과 연결됩니다.

  • 마지막 인스턴스의 변형을 수행 할 수 있습니다. 인스턴스를 가져 오는 대신 함수에서 인스턴스를 생성합니다. (그 동안 함수의 이름을 initInstance으로 변경해 봅시다. 특히 무엇을 얻는 지 신경 쓰지 않아야합니다. 새로운 Derived을 생성하고 이것을 하나의 참 인스턴스로 설정한다고합니다.)

그래서 (아직 불명 어떤 oddness을 금지), 그것은

class Base { 
    static Base * theOneTrueInstance; 

    public: 
    static Base & getInstance() { 
     if (!theOneTrueInstance) initInstance(); 
     return *theOneTrueInstance; 
    } 
    static void initInstance() { new Base; } 

    protected: 
    Base() { 
     if (theOneTrueInstance) throw std::logic_error("Instance already exists"); 
     theOneTrueInstance = this; 
    } 

    virtual ~Base() { } // so random strangers can't delete me 
}; 

Base* Base::theOneTrueInstance = 0; 


class Derived : public Base { 
    public: 
    static void initInstance() { 
     new Derived; // Derived() calls Base(), which sets this as "the instance" 
    } 

    protected: 
    Derived() { } // so we can't be instantiated by outsiders 
    ~Derived() { } // so random strangers can't delete me 
}; 

그리고 당신의 초기화 코드에서

, 당신은 당신이 입력에 따라 Base::initInstance(); 또는 Derived::initInstance();을 말 ... 같이 좀 밖으로 작동 싱글 톤을 원한다. 물론 Derived 특정 함수를 사용하기 위해서는 Base::getInstance()에서 반환 값을 캐스팅해야하지만 으로 재정의 된 가상 함수를 포함하여 Base에 정의 된 함수를 모두 사용할 수 있습니다. 그 일을이 방법도하지만, 자신의 단점들을 가지고

참고 :

  • 그것은 기본 클래스에 독신을 강요하는 부담의 대부분을 넣습니다. 기본에 이와 유사한 기능이없고 변경할 수없는 경우 다소 괴롭습니다. 각 클래스는 보호 소멸자를 선언 할 필요가, 또는 누군가가 따라 올 수 적절하게 (에) 캐스팅 후 하나 개의 인스턴스를 삭제 -

  • 기본 클래스는하지만, 책임의 모든을받을 수 없어 모든 것이 지옥에갑니다. 더 나쁜 것은 컴파일러가이를 적용 할 수 없다는 것입니다.

  • 우리가 보호하는 데스트 라크 터를 사용하여 인스턴스를 삭제하는 것을 방지하기 때문에 컴파일러가 더 두렵다면, 런타임도 프로그램이 종료 될 때 인스턴스를 제대로 삭제할 수 없습니다. 안녕히 가세요, RAII ... 안녕하세요. "메모리 누수가 감지되었습니다"라는 경고입니다. 물론 메모리는 궁극적으로 적절한 OS에 의해 재 확보 될 것입니다.그러나 소멸자가 실행되지 않으면, 당신은 당신을 위해 정리 작업을 할 수 없습니다. 종료하기 전에 일종의 정리 기능을 호출해야합니다. 그러면 RAII가 제공 할 수있는 보장과 가까운 곳에서 당신을 포기할 수 없습니다.)

  • IMO가 수행하는 initInstance 메소드가 표시됩니다 모든 사람이 볼 수있는 API에 실제로 속합니다. 원하는 경우 initInstance을 비공개로 만들고 init 함수를 friend으로 만들 수 있습니다. 그런 다음 클래스가 코드 외부에 대해 가정을하고 커플 링 문제가 다시 발생합니다.

위의 코드는 스레드로부터 안전하지 않습니다. 필요한 경우 스스로 해결할 수 있습니다.

진지하게, 덜 고통스러운 경로는 독신을 시행하려는 것을 잊어 버리는 것입니다. 인스턴스가 하나만 존재한다는 것을 확인하는 가장 복잡한 방법은 인 경우에만을 생성하는 것입니다. 여러 곳에서 사용해야하는 경우 종속성 삽입을 고려하십시오. (비 - 프레임 워크 버전은 "필요한 것을 물건에 물건을 넘기는"금액입니다. : P) 나는 싱글 톤과 상속에 대해 스스로 나 자신을 증명하려고 시도하고 위의 물건을 디자인하고 단지 나 자신에게 재앙을 재확인했습니다. 그 조합은 나는 실제 코드에서 실제로 그렇게하는 것을 추천하지 않는다.

+1

이것은 훌륭한 대답이며 철저하게 상세하고 다채로운 언어를 사용합니다 유머. SO 레이더로 날아간 것은 놀랍습니다. –