2011-07-23 15 views
6

ほとんどの人はデコレータパターンのピザ/コーフィーの例を知っています。デコレータパターンでの装飾の順序

Pizza* pizza1 = BigPizzaDecorator(MushromDecorator(SimplePizza())); 
Pizza* pizza2 = MushromDecorator(BigPizzaDecorator(SimplePizza())); 

あなたが非可換演算を持っている場合、2つのオブジェクトは、例えば、特に、ない完全に同じように動作しますが、:

BigPizzaDecorator::price() { return 10 + PizzaDecorator::price(); } // this is commutative 
BigPizzaDecorator::name() { return "big " + PizzaDecorator::name(); } // this is not commutative 

のでpizza1pizza2ための価格が同じですしかし、名前はありません。例えば、最初の数字は"Big mushroom pizza"、次の数字は"Mushroom big pizza"です。最初は英語が正しいです(おそらく "キノコ付きのビッグピザ"が良いでしょうが、あまり重要ではありません)。

著書「ヘッド最初の」ポイントアウトCOFEE例でこの問題:あなたはデコレータチェーンに複数の層で覗き見する必要があるとき

、あなた は、その真の意図を超えてデコレータをプッシュし始めています。

しかし、このようなことも可能です。最終decriptionを解析し、として「モカ、鞭、 モカ」印刷することができますCondimentPrettyPrint デコレータ想像「ムチ、ダブルモカ」を

それを行うための最善の方法は何ですか? operator<

+0

「pizza1.name()」と「pizza2.name()」に同じ出力が必要な点は重要ではありません。 –

答えて

5

デコレータを使用する場合、この種のものは必要とされませんでした。そして、あなたがこれを行う必要がある場合は、特に意図的に「デコレーターを意図を超えて押している」ように、デコレーターを使用しないでください。

私はこれを行う際に刺したが、コードは以下のとおりです。基本的には、デコレータが何を必要としているのかを理解しているオブジェクトの周りに薄いレイヤを作成してから、デコレータがデコレータをデコレートします。

ここでの主な問題は、出力の順序を維持するには、デコレータ間の関係を維持する必要があります。これはすぐにメンテナンスの悪夢になります。

#include <iostream> 
#include <queue> 
#include <sstream> 

struct name_part 
{ 
    std::string mName; 
    int   mPriority; 

    name_part(const std::string& name, int priority) 
    : mName(name) 
    , mPriority(priority) 
    { 
    } 
}; 

bool operator<(const name_part& a, const name_part& b) 
{ 
    return (a.mPriority < b.mPriority); 
} 

std::string priority_queueToString(const std::priority_queue<name_part>& orig) 
{ 
    std::ostringstream oss; 
    std::priority_queue<name_part> q(orig); 

    while (!q.empty()) 
    { 
     oss << q.top().mName << " "; 
     q.pop(); 
    } 

    return oss.str(); 
} 

struct SimplePizza 
{ 
    virtual std::string name() 
    { 
     return "pizza"; 
    } 
}; 

struct SimplePizzaImplementer : SimplePizza 
{ 
    SimplePizza *mDecorated; 

    SimplePizzaImplementer() 
    : mDecorated(0) 
    { 
    } 

    SimplePizzaImplementer(SimplePizza *decorated) 
    : mDecorated(decorated) 
    { 
    } 

    virtual std::string name() 
    { 
     return priority_queueToString(nameParts()); 
    } 

    virtual std::priority_queue<name_part> nameParts() 
    { 
     std::priority_queue<name_part> q; 

     if (mDecorated) 
     { 
      q.push(name_part(mDecorated->name(), 0)); 
     } 

     return q; 
    } 
}; 

struct MushroomDecorator : SimplePizzaImplementer 
{ 
    SimplePizzaImplementer *mDecorated; 

    MushroomDecorator(SimplePizzaImplementer *decorated) 
    : mDecorated(decorated) 
    { 
    } 

    virtual std::string name() 
    { 
     return priority_queueToString(nameParts()); 
    } 

    virtual std::priority_queue<name_part> nameParts() 
    { 
     std::priority_queue<name_part> q = mDecorated->nameParts(); 
     q.push(name_part("mushroom", 1)); 
     return q; 
    } 
}; 

struct BigDecorator : SimplePizzaImplementer 
{ 
    SimplePizzaImplementer *mDecorated; 

    BigDecorator(SimplePizzaImplementer *decorated) 
    : mDecorated(decorated) 
    { 
    } 

    virtual std::string name() 
    { 
     return priority_queueToString(nameParts()); 
    } 

    virtual std::priority_queue<name_part> nameParts() 
    { 
     std::priority_queue<name_part> q = mDecorated->nameParts(); 
     q.push(name_part("big", 2)); 
     return q; 
    } 
}; 

int main() 
{ 
    SimplePizzaImplementer *impl = new SimplePizzaImplementer(new SimplePizza()); 
    SimplePizza *pizza1 = new MushroomDecorator(new BigDecorator(impl)); 
    SimplePizza *pizza2 = new BigDecorator(new MushroomDecorator(impl)); 

    std::cout << pizza1->name() << std::endl; 
    std::cout << pizza2->name() << std::endl; 
} 
2

このようなコードをどこに置くかについては、オーバーロードされた演算子< <が適しています。

私は「デコレータを意図以上に押し込む」ことが本当にここで重視されると感じます。

あなたが本当に機能

"Mocha, Whip, Mocha" 

を解析し、その意図で公開されていないインタフェースから意味を推測している

"Whip, Double Mocha" 

概念的の策定に依存深刻なアプリケーションを構築するだろう。結果は非常に脆く、デコレータの実装のマイナーチェンジになります。「Yummy super mocha special」はパーサを破壊します。新しいデコレータを追加するには、未知のレベルの変更が必要です。

+0

+1、良好な分析。 – iammilind

+0

どうすれば実装できますか?'enum DecoratorEnum {MUSHROOM = 1、BIG = 2、...}'のようなenumを定義し、最初にソートしたファクトリに 'DecoratorEnum'のベクトルを渡してから、オブジェクトを返します飾られているので、最初に「キノコのデコレータ」がアプレイされます。たとえば、 'ピザ*ピザ=デコレートオブジェクト(SimplePizza、{BIG、MUSHROOM})'良い? –

+1

私の疑惑は、あなたが非可換的なデコレータを持っているなら、あなたはおそらくまた任意に再結合できないデコレータを持っているということです。だから(A、B、C =良い; B、A、C =悪い;しかし、A、CまたはB、Dについてはいかがですか)。私はあなたが印刷物のためにデコレータのパターンを落とす必要があると思う。 – djna