2011-09-12 9 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); 
} 

その無地のシンプルで:喜んでこれらの新たに生み出されたのiProduct提供する抽象工場、インタフェースを起動します抽象消費者、あなたには、いくつかの素敵な-きちんとインターフェースを持っていると仮定します。 しかし、ここでトリッキーな部分が来る。 は、2つ(またはそれ以上)の平行具体的なグループがあると仮定します。

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異なるものです。スペースシャトルの部品(パーツ工場とシャトル組立ライン)と野菜の袋(農場やイドクと一緒に、野菜を消費する人は誰ですか?)のように。しかし、彼らは一般的にそれを持っている:DoThings()。それがあなたが好きなものであれば、Pac​​kAndSend()、Serialize()、Dispose()のように振る舞います。何も具体的ではありませんが、階層を基盤とする合法です。 しかし、それらはまだ一般性よりも多くの違いがあります。だから、コンクールコンサルタントは違った使い方をしているのです。つまり、実際には、具体的なタイプと思われていることを絶対に確かめなければなりません。

ここに問題があります:私はその階層のユーザーに、現在の仮想優先で、IPoductをConcreteProductに強制的にダウンキャストしようとしています。そしてそれは私を苦労させている。私は何かが欠けていると感じる:階層の大きな欠陥、パターン知識の欠如、何か。 私は、ConcreteConsumerBは常にConcreteProductBを受信することを確認できますが、それでもダウンキャストです。そしてあなたはいつも(void *)の周りを回り、あなたが来ると思っている時にそれを投げるように強制するフレームワークを使っていますか?私はすでに考えられてきた

ソリューション:

  1. トンネルすべてconctrete interfeces IProductに。しかし、その製品のgonaは、Eat()、BeEaten()、Launch()、Destroy()、そして何が他のものかを知っている誰でも知ることができない制御不能なブロブに変わります。だから、この解決法は私に落ち着かせることよりも優れているようです。
  2. DoThings()は、IProductから別のハンドラに切り離されている可能性があります。このハンドラはすべてのConcret(Visitor-like)を受け入れることができます。そうすれば、IP製品は取り除かれ、別個の具体的なグループが作られます。しかし、それらの具体的なグループに共通の機能を実装している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); 
        } 
    } 
    

    このように私はそのダウンキャストをコントロールしていますが、それは残念です。

とにかく、この問題はおなじみですか?誰かがそれを見たか、あるいは英雄的にそれを自分で解決しましたか? C + +のソリューションは素晴らしいだろうが、私は統計的に型付けされた言語で十分だと思う。

+0

もしも* "reeealy different things" *は継承階層であり、工場は本当に仕事のために本当に適切なツールですか? – 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(); 

そこはおそらく、あなたがやりたいことのためのパターンではない、それはあなたが一緒に(専門用語)をsmooshingている可能性が高い二つのパターンです。これらのことがコードベースを共有する理由を裏切ったわけではないので、私たちは推測するしかありません。

もっと複雑なシナリオでは、作成から使用を厳密に分離したいと思うでしょう。類似したように見える異なるインターフェースを持つことは完全に有効ですが、異なる方法で使用されます。

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

ホットな階層に第2のレイヤーを導入する可能性があります。 IShuttleIProductから派生させ、そのグループを派生させます。次にの代わりにIShuttle*を生成するIShuttleProducerを追加します。これは大丈夫です.C++は仮想関数の共変な戻り値の型を許すからです...新しい戻り値の型が元の値から派生している限り、それは引き続きオーバーライドとみなされます。

しかし、デザインにはおそらくどちらかといえば再考が必要です。

関連する問題