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?
Example:

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.