Suppose there is an Entity class with the doSth () method and several decorators to it (EntityDecorator1, EntityDecorator2, etc.). Is it possible to make decorators have priority regardless of how decorators are applied to the class?

Entity e = new Entity(); e = new EntityDecorator1(e); e = new EntityDecorator2(e); e = new EntityDecorator3(e); e.doSth(); 

The priority of decorators is: EntityDecorator2, EntityDecorator1, EntityDecorator3 (ascending). Now, when calling e.doSth (), the method from EntityDecorator3 should be called first, then from EntityDecorator1, then from EntityDecorator2.

  • one
    What prevents simply decorating an object with the necessary sequence of decorators? - a_gura
  • one
    in this case, you don’t need a decorator at all; look towards chain of responsibility, or observer. in the latter it is easy to sort listeners by priorities before sending notifications - jmu

2 answers 2

Sketched quickly by hand, please do not strongly scold, such things must be carefully thought out. And the final result may vary from task. The idea is to create a decorator that aggregates other decorators with the right priority.

 #include <iostream> #include <map> using namespace std; //декорируемый класс class Entity { public: virtual void d() { cout <<"Entity"<<endl; } }; //классический декоратор class Decorator : public Entity { public: Decorator() :m_entity(NULL) { } explicit Decorator(Entity* ent) :m_entity(ent) { } virtual void d() { if(m_entity) m_entity->d(); } private: Entity* m_entity; }; //классический конкретный декоратор 1 class Decor1 : public Decorator { public: Decor1() { } explicit Decor1(Entity* ent) :Decorator(ent) { } virtual void d() { Decorator::d(); cout <<"Decor1"<<endl; } }; //классический конкретный декоратор 2 class Decor2 : public Decorator { public: Decor2() { } explicit Decor2(Entity* ent) :Decorator(ent) { } virtual void d() { Decorator::d(); cout <<"Decor2"<<endl; } }; //классический конкретный декоратор 3 class Decor3 : public Decorator { public: Decor3() { } explicit Decor3(Entity* ent) :Decorator(ent) { } virtual void d() { Decorator::d(); cout <<"Decor3"<<endl; } }; //вот он декоратор - комбинатор class CombineDecorator : public Decorator { public: explicit CombineDecorator(Entity* ent) { m_entities.insert(std::make_pair(100500, ent)); } void add(int order, Entity* ent) { m_entities.insert(make_pair(order, ent)); } void remove(int order, Entity* ent) { m_entities.erase(order); } void d() { for (EntityMap::iterator i = m_entities.begin(); i!= m_entities.end(); ++i) { i->second->d(); } } private: typedef std::map<int, Entity*> EntityMap; EntityMap m_entities; }; int main() { //обычное использование Entity* ent = new Entity(); ent = new Decor1(ent); ent = new Decor2(ent); ent->d(); cout <<"========"<<endl; //кобинатор отдельно Entity* ent2 = new Entity(); CombineDecorator* cd = new CombineDecorator(ent2); cd->add(3, new Decor1()); cd->add(4, new Decor2()); cd->add(2, new Decor3()); ent2 = cd; ent2->d(); cout <<"========"<<endl; //совместное использование cd->add(6, ent); ent2->d(); } 
  • This approach becomes meaningless when you are given some Entity* , which has already been decorated through CombineDecorator* , since for it the priorities of new decorators will not interfere with the priorities of old decorators. - Costantino Rupert
  • one
    @ Kotik_khohet_kusat agree, but this is not a bug, but a feature! This behavior was not required by the original task, was it? It seems that making a common priority for a single decoration hierarchy is not an unrealizable task. - Dith

Used a hybrid of solutions @jmu and @Dith . Entity wrapped in a container in which he stored a list of decorators sorted by priority. Yes, it turned out not quite a decorator, but it works and looks quite nice.