2011-09-12 4 views
1

나는 잃어버린 일부 신성한 지침이 필요합니다.abstact hierarcy의 구체적인 인스턴스에 대한 계약 시행

중요한 것부터 먼저 : 당신은 몇 가지 잘-깔끔한 인터페이스를 가지고 가정

class IProduct 
{ 
public: 
    virtual void DoThings(); 
} 


enum ProductType 
{ 
    ... 
} 


class IProducer 
{ 
public: 
    virtual IProduct* Produce(ProductType type); 
} 


class IConsumer 
{ 
public: 
    virtual void Consume(IProduct* product); 
} 

그것의 일반 간단하면서도 : 추상적 인 공장, 기꺼이 그 갓 산란 ​​IProducts에서 제공하는 인터페이스를 호출합니다 추상적 인 소비자. 하지만 여기 까다로운 부분이 있습니다. 가 두 개 (또는 그 이상)의 병렬 콘크리트 그룹입니다한다고 가정

class ConcreteProducerA : public IProducer { ... } 
class ConcreteConsumerA : public IConsumer { ... } 
class ConcreteProductA : public IProduct { ... } 

class ConcreteProducerB : public IProducer { ... } 
class ConcreteConsumerB : public IConsumer { ... } 
class ConcreteProductB : public IProduct { ... } 

그리고 그 콘크리트가 reeealy 다른 것입니다. 우주 왕복선 부품 (부품 공장과 셔틀 조립 라인이 있음)과 야채 봉지 (농장과 .. idk와 함께 누가 그 야채를 소비합니까?)처럼. 그러나 그들은 DoThings()과 같은 일반적인 것을 가지고 있습니다. 원하는대로 PackAndSend(), Serialize() 또는 Dispose()와 같은 척하십시오. 구체적인 것은 아니지만 계층을 기반으로하는 합법적 인 것입니다. 그러나 그것들은 일반성보다 더 많은 차이점을 가지고 있습니다. 그래서 그 ConcreteConsumers는 다르게 그들을 사용하는 경향이 있습니다. 그래서 다르게, 실제로, 그들은 절대적으로 확실해야합니다, 그것은 콘크리트 타입이라고 가정합니다.

그래서 여기에 문제가 있습니다 : 나는 그 계층의 사용자가 지금 IPoduct를 그들의 가상 오버라이드에서 ConcreteProduct로 다운 캐스트하도록하고 있습니다. 그리고 저를 괴롭히는 것이 어렵습니다. 나는 내가 무언가를 놓치고 있다고 느낍니다 : 계급의 큰 결함, 패턴 지식의 부족, 무언가. 제 말은 ConcreteConsumerB가 항상 ConcreteProductB를 수신한다는 것을 확인할 수 있습니다.하지만 여전히 다운 캐스트입니다. 그리고 당신은 언제나 (void *)의 주위를 돌아 다니며, 나중에 올 것이라고 생각할 때 그것을 캐스팅하도록하는 프레임 워크를 사용했을 것입니까?

솔루션은 내가 이미 고려했습니다 i- 제품에

  1. 터널 모든 conctrete의 interfeces. 하지만 그 제품 gona는 Eat(), BeEaten(), Launch(), Destroy() 및 그 밖의 무엇을 알고있는 사람이 통제 할 수없는 얼룩으로 바뀝니다. 그래서이 해결책은 저를 배반하는 것보다 나은 것 같습니다.
  2. DoThings()은 IProduct에서 다른 핸들러로 분리 될 수 있습니다. 그러면 모든 Concret (Visitor-like)을 수용 할 수 있습니다. 그렇게하면 IProduct를 제거 할 수 있고 별도의 구체적인 그룹이 생깁니다. 하지만 SemiConcrete 레이어가 있으면 이러한 구체적인 그룹에 공통적 인 기능을 구현할 수 있습니까? 라벨링, 모핑, 마사지 등. 또한 다른 구체적인 그룹을 추가해야 할 경우 해당 방문자를 변경해야합니다.
  3. (ab) 템플릿을 사용하십시오. 그것은 현재 현명 해 보인다.

    의 라인을 따라 뭔가
    template < typename _IProduct > 
    class IConcreteProducer : public IProducer 
    { 
    public: 
        virtual _IProduct* Produce(_IProduct::Type type) = 0; 
        virtual _IProduct::Type DeduceType(ProductType type) = 0; 
        virtual IProduct* Produce(ProductType type) 
        { 
         return IConcreteProducer<typename _IProduct>::Produce(DeduceType(type)); 
        } 
    } 
    
    template < typename _IProduct > 
    class IConcreteConsumer : public IConsumer 
    { 
    public: 
        virtual void Consume(_IProduct* product) = 0; 
        virtual void Consume(IProduct* product) 
        { 
         IConcreteConsumer<typename _IProduct>::Consume((_IProduct*)product); 
        } 
    } 
    

    이 방법은 내가 다운 캐스트를 통제하고 있지만, 그것은 stil 선물입니다.

어쨌든이 문제는 누구에게 친숙합니까? 누군가 그것을 풀어 본 것입니까, 아니면 영웅적으로 해결 한 사람입니까? C++ 솔루션은 훌륭 하겠지만 통계적으로 형식화 된 언어이면 충분할 것입니다.

+0

* "재주문이 다른 것"*이 상속 계층 구조이고 공장에서 실제 작업에 적합한 도구가 충분합니까? – Flexo

+0

이것은 분명히 잘못되었습니다 :'IConcreteConsumer ' – Nawaz

+0

@awoodland 우물쭈물은 다운 캐스트를 요구할 정도로 충분히 다르지만 최소한 코드를 재사용하기 위해서 (적어도 의미는 별개로) 공통 기반에서 파생되기에 충분합니다. Gracefuly를 파생어 (아직 OOP 스타일 유지)와 별도로 재사용 할 수있는 또 다른 방법이 있습니까? – Dark

답변

2

그러나 그들은 일반적으로 DoThings()을 사용합니다. 그것이 무엇이든간에, PackAndSend() 또는 Serialize() 또는 Dispose()와 같은 을 가장한다고 가정하십시오. 구체적인 것은 없지만 계층을 기반으로 할 수 있습니다.

일부 계층 구조에서, 그들이해야 의미하지 않는다 할 수있다해서. 그들은 무관하다. 나는 셔틀과 야채를 일반화함으로써 어떤 코드 기반에 어떤 가치를 더하고 있는지를 짐작할 수 없다. 사용자에게 이점이 없으면 혼자서 상황이 복잡해질 가능성이 큽니다.

다음과 같은 인터페이스가 필요합니다. 그들은 아무것도 상속받지 않는다는 것을 주목하십시오. 코드를 공유했다면 사람들이 작곡을 통해 재사용 할 수있는 더 단순한 벙어리 구체적인 클래스를 작성하십시오.

template<typename T> 
    class Producer { 
    public: 
    virtual ~Producer() {} 
    virtual std::auto_ptr<T> produce() = 0; 
    }; 

template<typename T> 
    class Consumer { 
    public: 
    virtual ~Consumer() {} 
    virtual void consume(std::auto_ptr<T> val) = 0; 
    }; 

그런 다음 다양한 소스에서 이들을 만드는 구체적인 기능을 기대합니다.

typedef Producer<Shuttle> ShuttleProducer; 
typedef Consumer<Shuttle> ShuttleConsumer; 

std::auto_ptr<ShuttleProducer> GetShuttleProducerFromFile(...); 
std::auto_ptr<ShuttleProducer> GetShuttleProducerFromTheWeb(...); 
std::auto_ptr<ShuttleProducer> GetDefaultShuttleProducer(); 

아마도 당신이하고 싶은 패턴이 없을 것입니다. 당신이 함께 쓰는 패턴이 두 가지 일 것입니다. 이러한 것들이 코드 기반을 공유해야하는 이유를 배신하지 않았기 때문에 우리는 추측 할 수 있습니다.

더 복잡한 시나리오에서는 사용과 작성을 엄격하게 구분해야합니다. 비슷하게 생겼지 만 다르게 사용되는 다른 인터페이스를 갖는 것은 완전히 유효합니다.

class Foo { 
public: 
    virtual ~Foo() {} 
    virtual void doStuff() = 0; 
    virtual void metamorphose() = 0; 
}; 

class Fu { 
public: 
    virtual ~Fu() {} 
    virtual void doStuff() = 0; 
    virtual void transmorgrify() = 0; 
}; 
2

하나의 가능성은 두 번째 계층을 뜨거운 계층 구조에 도입하는 것입니다. IShuttleIProduct에서 파생시키고 그 그룹을 파생시킵니다. 그런 다음 IProduct* 대신 IShuttle*이 나오는 IShuttleProducer을 추가하십시오. C++이 가상 함수에 대한 공변 반환 유형을 허용하기 때문에 이것은 괜찮습니다 ...새 반환 유형이 원본에서 파생되는 한 계속 재정의로 간주됩니다.

하지만 디자인에 다소간 재고가 필요합니다.

관련 문제