2010-04-29 2 views
3

C++에서 인터페이스는 모든 메소드가 순수 가상 인 클래스에 의해 구현 될 수 있습니다.C++에서 비 가상 인터페이스 관용구를 사용하여 인터페이스 클래스를 구현하는 방법은 무엇입니까?

class Lib::IFoo 
{ 
    public: 
     virtual void method() = 0; 
}; 

:

이러한 클래스는 객체 라이브러리에서 다른 클래스와 함께 작동 할 수 있도록 구현해야하는지 방법을 설명하는 라이브러리의 일부가 될 수있는 지금

class Lib::Bar 
{ 
    public: 
     void stuff(Lib::IFoo &); 
}; 

I Lib :: Bar 클래스를 사용하여 IFoo 인터페이스를 구현해야합니다.

class FooBase : public IFoo // implement interface IFoo 
{ 
    public: 
     void method(); // calls methodImpl; 

    private: 
     virtual void methodImpl(); 
}; 

비 가상 인터페이스 (NVI) 관용구 해야지 : 나는 NVI 관용구를 사용하여 일반적인 동작을 보장하는 기본 클래스와 함께 일하고 싶습니다 그래서 내 목적을 위해

나는 관련 클래스의 전체 필요 파생 클래스가 FooBase::method()에 구현 된 공통 동작을 재정의 할 가능성을 거부했지만 IFoo 이후로 가상 클래스를 만들었으므로 모든 파생 클래스가 FooBase::method()을 무시할 수있는 것으로 보입니다.

NVI 관용어를 사용하고 싶다면, 이미 제안 된 pImpl 관용구 이외의 옵션은 무엇입니까 (감사합니다 space-c0wb0y).

답변

4

일반적으로 NVI ("템플릿 방법"이라고도 함)를 사용하는 이유는 파생 클래스가 부분의 기본 클래스 동작 인 만 변경해야한다는 것입니다. 그래서 당신이 할 것은 이것이다 :

class base { 
    public: 
    void f() 
    { 
     // do something derived classes shouldn't interfere with   
     vf(); 
     // do something derived classes shouldn't interfere with   
     vg(); 
     // do something derived classes shouldn't interfere with   
     vh(); 
     // do something derived classes shouldn't interfere with   
    } 
    private: 
    virtual void vf(); // might be pure virtual, too 
    virtual void vg(); // might be pure virtual, too 
    virtual void vh(); // might be pure virtual, too 
}; 

파생 클래스는 그들에게 의미있는 장소에서 f()에 연결하고 기본적인 알고리즘을 엉망으로하지 않고, 측면을 f()의 행동을 변경할 수 있습니다.

+0

사실, 템플릿 클래스가 제공하는 보증 (예 : vf, vg 및 vh의 실행 순서)을 잃지 않고 기본 클래스에서 인터페이스를 구현할 수 있습니까? –

+0

@andreas : 마이클의 대답에 대한 귀하의 의견에 답변했습니다. 귀하의 질문을 확장하여 귀하가하고 싶은 것을 조금 더 잘 설명하도록 제안드립니다. 내가 이해하는 바에 따르면, 아마도 당신이 찾고있는 것이 믹스 인이나 정책 일 것입니다. – sbi

+1

NVI와 템플릿 방법은 완전히 다른 개념입니다. NVI는 일반적으로 Template Method가 일반적으로 구현되는 방법입니다. 가상 객체를위한 전략 객체를 사용하는 템플릿 메소드를 간단히 가질 수 있으며 템플릿 메소드 패턴이 아닌 NVI 패턴을 가질 수도 있습니다. –

5

난 당신이 잘못된 방향으로 주위에 당신의 NVI 패턴을 가지고 생각 : 그 그래도 문제를 해결 http://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Non-Virtual_Interface

확실하지합니다.

class IFoo 
{ 
    public: 
     void method() { methodImpl(); } 
    private: 
     virtual void methodImpl()=0; 
}; 

class FooBase : public IFoo // implement interface IFoo 
{ 
    private: 
     virtual void methodImpl(); 
}; 

다음은 DB에서 XML과 서로 읽는 판독기를 사용하여이 작업을 수행 할 수있는 이유의 예입니다. 공통 구조는 NVI readFromSource로 이동되며, 비 공통적 인 동작은 개인 가상 getRawDatum으로 이동됩니다. 이 방법은 로깅 및 오류 검사가 하나의 기능에서만 필요합니다.

class IReader 
{ 
    public: 
    // NVI 
    Datum readFromSource() 
    { 
     Datum datum = getRawDatum(); 
     if(! datum.isValid()) throw ReaderError("Unable to get valid datum"); 
     logger::log("Datum Read"); 
     return datum; 
    } 
    private: 
    // Virtual Bits 
    Datum getRawDatum()=0; 
}; 

class DBReader : public IReader 
{ 
    private: 
    Datum getRawDatum() { ... } 
}; 

class XmlReader : public IReader 
{ 
    private: 
    Datum getRawDatum() { ... } 
}; 
당신이 달성하기 위해 pimpl - 관용구를 사용할 수
+0

NVI 관용구 (일명 템플릿 방법)를 사용하여 파생 클래스의 동작에 공통성을 보장하는 기본 클래스와 해당 인터페이스의 구현이 제공해야하는 메서드를 * only * 규정하는 인터페이스 클래스로 작업하고 싶습니다. . 두 개의 완전히 다른 기본 클래스를 가질 수 있습니다. 하나는 XML 파일과 인터페이스하고 다른 하나는 RDBMS와 인터페이스합니다. 그들은 IFoo를 구현하고 파생 된 클래스의 동작에 공통점을 보장해야합니다. –

+0

@andreas : C++에서 인터페이스는 (아마도 순수한) 가상 함수를 선언하는 기본 클래스를 사용하여 설명됩니다. 그런 다음 파생 클래스는 이러한 가상 함수를 재정 의하여 구현합니다. 기본 클래스의 모든 가상 멤버 함수는 파생 클래스에서 재정의 될 수 있습니다. 그러므로 NVI는 _non-virtual_ 메소드를 구현하여 _base_ 클래스에서만 구현 될 수 있습니다. 2 계층 접근법을 원한다면 인터페이스에서 파생 된 공통 기본 클래스에서 파생 된 클래스가 모든 인터페이스의 가상 함수를 무시할 수 있으므로 간단한 NVI가 작동하지 않습니다. – sbi

+0

@andreas 귀하의 의견에 귀하가 기술 한 것을 수행하는 예제를 추가했습니다. 희망은 그 말이 맞습니다. 나는 당신이 이것보다 더 많이 원한다라고 생각한다.. 그러나 나는 정확하게 무엇이 확실하지 않다. 어쩌면 당신은 1) 당신이 할 수 있기를 원하는 것, 그리고/또는 2) 정상적인 NVI에 무엇이 잘못 되었는가의 전체 예제를 가지고 질문을 더 확장시켜야 할 것입니다. –

1

:

이제
class IFoo 
{ 
    public: 
     IFoo(boost::shared_ptr<IFooImpl> pImpl) 
      : m_pImpl(pImpl) 
     {} 

     void method() { m_pImpl->method(); } 
     void otherMethod() { m_pImpl->otherMethod(); } 
    private: 
     boost::shared_ptr<IFooImpl> m_pImpl; 
}; 

class IFooImpl 
{ 
    public: 
     void method(); 
     virtual void otherMethod(); 
}; 

다른 사람들이 여전히 IFooImpl를 서브 클래스와 IFoo에 전달할 수 있지만이 method의 동작을 재정의 할 수 없습니다 (그들은 otherMethod을 대체 할 수 있습니다). IFooImplIFoo의 직접 하위 클래스로 만들고 enable_shared_from_this을 사용하여 IFoo을 올바르게 초기화 할 수도 있습니다. 이것은 방법의 요지에 불과합니다. 이 방법을 조정할 수있는 방법은 여러 가지가 있습니다. 예를 들어 factory-pattern을 사용하여 IFoo이 올바르게 작성되었는지 확인할 수 있습니다.

희망이 있습니다.

+0

흠 .. 이것은 오버 헤드와 기계가 개인 가상 기능을 사용하지 않도록하는 것처럼 보입니다. –

+0

문제는 모든 가상 함수, 심지어 개인용 서브 클래스도 서브 클래스에 의해 재정의 될 수 있다는 것입니다. 이 옵션에는 많은 상용구가 있지만 사실이지만 방탄입니다. 따라서 그것은 절충점입니다. –

2

메소드가 기본 클래스에서 가상으로 선언되면 메소드는 virtual 키워드가 사용되지 않아도 모든 파생 클래스에서 자동으로 가상이됩니다. 따라서 귀하의 예에서는 FooBase의 두 가지 방법 모두 가상입니다.

... 파생 클래스에게 FooBase :: 방법()에 구현 된 일반적인 동작을 오버라이드 (override)의 가능성을 거부 할 ...

당신이 IFoo 제거 할 수있는 경우

, 그냥 계층 구조를 FooBase으로 시작하고 가상이 아닌 가상 번호 method으로 시작하십시오. 그러나 IFoo의 직계 자녀가 method()을 무시할 수 있지만, FooBase의 어린이는이를 무시해야합니다. 나는 그것이 가능하다고 생각하지 않는다.