Ordering ornaments in an ornamental pattern

Most of you know the pizza / cofee example for drawing a decorator.

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

two objects behave in a similar way, but not completely, in particular, if you have a non-commutative operation, for example:

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

Thus, the price for pizza1 and pizza2 same, but the name is missing, for example, the first should be "Big mushroom pizza" , the second "Mushroom big pizza" . The first one is English correct ("Big pizza with mushrooms" is probably better, but this is not so important).

In the book "Head First", indicate this problem with the Cofee example:

When you need to look at several layers in a chain of decorators, you begin to push the decorator beyond his true intent.

However, such things are possible. Imagine a CondimentPrettyPrint decorator that parses the final decription and can print “Mocha, Whip, Mocha” as “Whip, Double Mocha”.

What is the best way to do this? ( operator< ?)

+6
source share
2 answers

I never knew this kind of thing to use when using decorators. And I would think that if you need to do this, then you should not use decorators, especially since you deliberately "push the decorator beyond his intent."

I had a hit to do this, the code below. Basically, I create a thin layer around the SimplePizza object, which understands what decorators need, then decorators decorate it.

The main problem here is that in order to maintain order in the output, you will need to maintain relationships between decorators, which can quickly become a nightmare for maintenance.

 #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; } 
+5
source

In terms of where to put such code, having an overloaded operator <<is possible.

I feel that “pushing the decorator beyond his intention” is really necessary here.

You really would create a serious application, the functioning of which depends on parsing

 "Mocha, Whip, Mocha" 

and wording

 "Whip, Double Mocha" 

Conceptually, you derive semantics from an interface that is not published with this intent. The result will be very fragile, minor changes in the implementation of decorators: "Yummy super mocha special" would break the parser. Unknown change levels will be required to add new decorators.

+2
source

Source: https://habr.com/ru/post/893406/


All Articles