Pagina 1 di 2 1 2 ultimoultimo
Visualizzazione dei risultati da 1 a 10 su 13
  1. #1
    Utente di HTML.it L'avatar di ing82
    Registrato dal
    Sep 2014
    Messaggi
    177

    Design Pattern Observer

    Sto sperimentando il design pattern Observer seguendo le indicazioni riportate nel libro Design Patterns di Gamma, Helm, Johnson, Vlissides che qui riporto:

    codice:
    class Subject;
    
    class Observer
    {
      public:
        virtual ~Observer(){};
        virtual void update(Subject*subject)=0;
      protected:
        Observer(){};
    };
    
    class Subject
    {
      public:
        virtual ~Subject(){};
        virtual void attach(Observer*observer);
        virtual void detach(Observer*observer);
        virtual void notify();
      protected:
        Subject(){};
      private:
        std::vector<Observer*>mObserverList;
    };
    Per le classi base quindi niente problemi.

    Mi crea qualche problema / dubbio come implementare le classi derivate, in particolare di Observer.
    In Observer infatti devo tenere il riferimento al / ai Subject che sto osservando.

    Ho provato a fare cosi':



    codice:
    class DerSubject: public Subject {};
    
    class ObserverTester: public Observer
    /*ObeserverTester deve tenere d'occhio un DerSubject, quindi al suo interno, e' meglio fare riferimento a un DerSubject o a un Subject?*/
    {
      public:
        ObserverTester(Subject* subject)//o DerSubject?
        {
          mSubject=subject;
          mSubject->attach(this);
        }; 
        void update(Subject* subject) override//qui resta cosi' per forza...
        {
          if(subject==mSubject)
          {
            std::cout<<"\nHello from observer "<<this;
            std::cout<<"\nI'm observing subject "<<mSubject;
          }
        };
      private:
        Subject* mSubject;//o DerSubject?
    };
    Il dubbio principale e' se nelle classi derivate da Observer tengo come riferimento un dato membro di tipo Subject o DerSubject, cioe' quello che sara' il reale tipo di subject osservato: credo la seconda, anche se nell'esempio di codice sopra postato ho fatto il contrario.

    Inoltre, potrebbe mai essere necessario implementare un metodo setSubject nelle classi derivate da Observer per poter cambiare il Subject osservato?

    Grazie
    Ultima modifica di ing82; 04-12-2016 a 16:49

  2. #2
    Utente di HTML.it L'avatar di shodan
    Registrato dal
    Jun 2001
    Messaggi
    2,381
    Il dubbio principale e' se nelle classi derivate da Observer tengo come riferimento un dato membro di tipo Subject o DerSubject, cioe' quello che sara' il reale tipo di subject osservato: credo la seconda, anche se nell'esempio di codice sopra postato ho fatto il contrario.
    No, è la prima. Passando un puntatore a classe base perdi le informazioni su cosa sia la classe derivata, cosa che un puntatore a DerSubject richiede. Ricorda che un observer può osservare diversi subject (ragione per cui si passa un puntatore a subject in update() ), come un subject può avere diversi observer e non è possibile (in teoria) tenere traccia di tutte le possibili implementazioni.

    Inoltre, potrebbe mai essere necessario implementare un metodo setSubject nelle classi derivate da Observer per poter cambiare il Subject osservato?

    Lo vedo poco utile. Ogni volta che devi osservare un subject diverso devi andare di setSubject.
    Personalmente aggiungerei due metodi virtuali addSubject(Subject*) / removeSubject(Subject*) che mi consentano l'inserimento in un std::set<> delle varie istanze subject che voglio controllare in update().
    In codice:
    codice:
    class Subject;
    
    class Observer
    {
      public:
        virtual ~Observer(){};
        virtual void update(Subject*subject)=0;
        virtual void addSubject(Subject* sub)=0;
        virtual void removeSubject(Subject* sub)=0;
      protected:
        Observer(){};
    };
    
    class Subject
    {
      public:
        virtual ~Subject(){};
        virtual void attach(Observer*observer);
        virtual void detach(Observer*observer);
        virtual void notify();
      protected:
        Subject(){};
      private:
        std::set<Observer*>mObserverList;
    };
    
    
    class DerSubject: public Subject {};
    
    class ObserverTester: public Observer
    {
      public:
        ObserverTester(Subject* subject)
        {
                addSubject(subject);
        }; 
        
        void addSubject(Subject* sub) {
          sub=subject;
          sub->attach(this);
            mSubjects.insert(sub);
        }
    
        void removeSubject(Subject* sub) {
          sub->dettach(this); 
            mSubjects.remove(sub);
        }
        
        
        void update(Subject* subject) override
        {
          
          auto sub = mSubject.find(subject);
          if(sub != mSubjects.end())
          {
            std::cout<<"\nHello from observer "<<this;
            std::cout<<"\nI'm observing subject "<<sub;
          }
        };
      private:
        std::set<Subject*> mSubjects;
    };
    This code and information is provided "as is" without warranty of any kind, either expressed
    or implied, including but not limited to the implied warranties of merchantability and/or
    fitness for a particular purpose.

  3. #3
    Utente di HTML.it L'avatar di ing82
    Registrato dal
    Sep 2014
    Messaggi
    177
    Quote Originariamente inviata da shodan Visualizza il messaggio
    No, è la prima. Passando un puntatore a classe base perdi le informazioni su cosa sia la classe derivata, cosa che un puntatore a DerSubject richiede. Ricorda che un observer può osservare diversi subject (ragione per cui si passa un puntatore a subject in update() ), come un subject può avere diversi observer e non è possibile (in teoria) tenere traccia di tutte le possibili implementazioni.
    Ok. Non capisco allora come poter ricavare i dati che mi servono dai subject per poter aggiornare l'observer tramite il metodo update.

    Il caso reale che mi ha portato ad usare il design pattern observer è un oggetto che dipende da altri due, nel senso che se modifico uno dei due, o entrambi, l'oggetto 'indipendente' si deve aggiornare in automatico.

    Il design pattern observer mi sembrava adatto allo scopo.

    Avro' quindi due subject, che sono di due classi diverse, perchè rappresentano due cose diverse, in cui registrerò il mio observer (DerSubject1 e DerSubject2).

    Nell'observer, dovrò quindi tenere traccia di entrambi i subject osservati, che non varieranno di numero.

    Se nell'observer memorizzo i due subject come Subject, non riesco ad accedere 'direttamente' (bisogna fare cast) ai metodi implementati nella classe derivata, che sono quelli che permettono di ricavare i dati necessari all'aggiornamento dell'observer.

    Se li memorizzo come DerSubject1 e DerSubject2 riesco invece ad accedere direttamente ai metodi necessari a ricavare i dati necessari all'observer.

    Potrebbe sembrare esagerato usare il d.p. observer per una cosa del genere, in realtà gli stessi due subject potrebbero dover essere osservati, e quindi notificare eventuali modifiche, da un altro observer, 'concettualmente' diverso dal precedente (sarebbero quindi DerObserver1 e DerObserver2).

    Inoltre, la modifica di uno dei due subject, richiederebbe l'aggiornamento di altri tipi di observer, che non ho ancora implementato, ma che lo dovranno essere a breve, ecco il perchè del tentativo di ricorrere al d.p. observer.

  4. #4
    Utente di HTML.it L'avatar di shodan
    Registrato dal
    Jun 2001
    Messaggi
    2,381
    Quote Originariamente inviata da ing82 Visualizza il messaggio
    Avro' quindi due subject, che sono di due classi diverse, perchè rappresentano due cose diverse, in cui registrerò il mio observer (DerSubject1 e DerSubject2).
    Nell'observer, dovrò quindi tenere traccia di entrambi i subject osservati, che non varieranno di numero.
    Ok, i subject sono due. A questo punto il parametro della funzione update() serve solo a stabilire quale subject usare per aggiornare i dati dell'observer. A questo punto dovresti modificare update() così:
    codice:
          if(subject==mSubject1)
          {
            std::cout<<"\nHello from observer "<<this;
            std::cout<<"\nI'm observing subject "<<mSubject1;
            parametro1 = mSubject1->metodo1(); // esempio
          }
    
          if(subject==mSubject2)
          {
            std::cout<<"\nHello from observer "<<this;
            std::cout<<"\nI'm observing subject "<<mSubject2;
            parametro2 = mSubject2->metodo2(); // esempio
          }
    Inoltre non puoi passare nel costruttore (o altra funzione preposta) un generico Subject*. Devi passare proprio DerSubject1* e DerSubject2* se non vuoi perdita di informazioni.

    ...ecco il perchè del tentativo di ricorrere al d.p. observer.
    Beh, è il suo mestiere, no?
    This code and information is provided "as is" without warranty of any kind, either expressed
    or implied, including but not limited to the implied warranties of merchantability and/or
    fitness for a particular purpose.

  5. #5
    Utente di HTML.it L'avatar di ing82
    Registrato dal
    Sep 2014
    Messaggi
    177
    Quote Originariamente inviata da shodan Visualizza il messaggio
    A questo punto il parametro della funzione update() serve solo a stabilire quale subject usare per aggiornare i dati dell'observer.
    Ok, come pensavo.



    Quote Originariamente inviata da shodan Visualizza il messaggio
    Inoltre non puoi passare nel costruttore (o altra funzione preposta) un generico Subject*. Devi passare proprio DerSubject1* e DerSubject2* se non vuoi perdita di informazioni.
    e di conseguenza anche i due dati membro deputati a tenere il riferimento ai due osservati, saranno del tipo derivato, DerSubject1* e DerSubject2*...

    Grazie

  6. #6
    Utente di HTML.it L'avatar di shodan
    Registrato dal
    Jun 2001
    Messaggi
    2,381
    Beh, si. Tra l'altro questo codice mi ha dato modo di approfondire il pattern visitor col double dispatching. Grazie.
    This code and information is provided "as is" without warranty of any kind, either expressed
    or implied, including but not limited to the implied warranties of merchantability and/or
    fitness for a particular purpose.

  7. #7
    Utente di HTML.it L'avatar di ing82
    Registrato dal
    Sep 2014
    Messaggi
    177
    Possibile implementazione di un Observer che osserva piu' di un Subject.

    Chiedo, a chi vorra' darci un occhio, di porre particolare attenzione al metodo setSubject, io non trovo altre strade, se qualcuno ne ha di migliori...devo ancora approfondire quanto proposto da Shodan mediante std::set, che non conosco e devo studiare, ma non credo sia adatto se serve avere direttamente i tipi di Subject derivati e non il Subject generico (v. post precedenti).
    Si accettano suggerimenti e/o modifiche.

    codice:
    class DerObserverMoreSubject: public Observer
    {
      public:
        DerObserverMoreSubject(DerSubject1* subject1, DerSubject2* subject2, DerSubject3* subject3):
        Observer(), mSubject1(subject1), mSubject2(subject2), mSubject3(subject3)
        {
          if(mSubject1)
          {
            mSubject1->attach(this);
          }
          if(mSubject2)
          {
            mSubject2->attach(this);
          }
          if(mSubject3)
          {
            mSubject3->attach(this);
          }
        };
        ~DerObserverMoreSubject()
        {
          if(mSubject1)
          {
            mSubject1->detach(this);
          }
          if(mSubject2)
          {
            mSubject2->detach(this);
          }
          if(mSubject3)
          {
            mSubject3->detach(this);
          }
        };
        void setSubject(Subject* subject) override
        {
          bool found = false;
          if(!found)
          {
            DerSubject1* temp = dynamic_cast<DerSubject1*>(subject);
            if(temp)
            {
              mSubject1 = temp;
              std::cout<<"\nOperazione andata a buon fine\n";
              found = true;
            }
          }
          if(!found)
          {
            DerSubject2* temp = dynamic_cast<DerSubject2*>(subject);
            if(temp)
            {
              mSubject2 = temp;
              std::cout<<"\nOperazione andata a buon fine\n";
              found = true;
            }
          }
          if(!found)
          {
            DerSubject3* temp = dynamic_cast<DerSubject3*>(subject);
            if(temp)
            {
              mSubject3 = temp;
              std::cout<<"\nOperazione andata a buon fine\n";
              found = true;
            }
          }
          if(!found)
          {
            std::cout<<"\nPassato un Subject di tipo diverso dall'atteso, l'operazione non e' andata a buon fine\n";
          }
        };
        void update(Subject* subject) override
        {
          std::cout<<"\nHello from Observer "<<this<<" \n";
          if(subject == mSubject1)
          {
            std::cout<<"\n"<<__func__<<" was called by mSubject1\n";
          }
          if(subject == mSubject2)
          {
            std::cout<<"\n"<<__func__<<" was called by mSubject2\n";
          }
          if(subject == mSubject3)
          {
            std::cout<<"\n"<<__func__<<" was called by mSubject3\n";
          }
        };
        void removeSubject(Subject* subject) override
        {
          if(subject == mSubject1)
          {
            mSubject1 = nullptr;
          }
          if(subject == mSubject2)
          {
            mSubject2 = nullptr;
          }
          if(subject == mSubject3)
          {
            mSubject3 = nullptr;
          }
        };
      private:
        DerSubject1* mSubject1;
        DerSubject2* mSubject2;
        DerSubject3* mSubject3;
    };

  8. #8
    Utente di HTML.it L'avatar di shodan
    Registrato dal
    Jun 2001
    Messaggi
    2,381
    Ho come l'impressione di essermi perso qualcosa. Si era parlato di due subject, ora sono tre. E ho l'impressione che possano aumentare.
    Inoltre, la modifica di uno dei due subject, richiederebbe l'aggiornamento di altri tipi di observer, che non ho ancora implementato, ma che lo dovranno essere a breve, ecco il perchè del tentativo di ricorrere al d.p. observer.
    Qui parli di più observer. Immagino che debbano interagire con i tre (o più) subject, oppure ho capito male?
    se qualcuno ne ha di migliori...
    Ce ne sono di migliori (almeno dal punto di vista della OOP), ma per discuterne occorre sapere di quanta roba di un tipo e dell'altro stiamo parlando. Perchè se parliamo di al massimo tre subject, si può violare leggermente il pattern. Se parliamo da quattro in su meglio mixare l'obvserver al pattern Visitor.
    This code and information is provided "as is" without warranty of any kind, either expressed
    or implied, including but not limited to the implied warranties of merchantability and/or
    fitness for a particular purpose.

  9. #9
    Utente di HTML.it L'avatar di ing82
    Registrato dal
    Sep 2014
    Messaggi
    177
    Quote Originariamente inviata da shodan Visualizza il messaggio
    Ho come l'impressione di essermi perso qualcosa. Si era parlato di due subject, ora sono tre. E ho l'impressione che possano aumentare.
    Direi di si, ma la colpa e' mia: il caso reale necessitava di due subject, di cui tenere traccia del tipo concreto in modo da poter accedere ai suoi metodi senza dover eseguire operazioni di cast, il caso postato e' un caso 'accademico', indipendente, che parte sempre dalla stessa necessita', cioe' piu' subject osservati di tipo diverso a cui poter accedere per richiamare i rispettivi metodi, che pero' mi e' servito ad impostare un piccolo programmino di prova della corretta implementazione del pattern Observer: son diventati tre subject per puro caso, ho implementato il tutto a distanza di tempo dall'inizio della discussione e per tenere un filo logico avrei dovuto tenere due subject, spero di aver chiarito.

    In particolare mi interessava sapere se il modo in cui individuo il tipo del subject passato col metodo setSubject e' corretto (uso del dynamic_cast) o si puo' fare altrimenti, usando ad esempio typeid, ma da quanto letto in giro mi pare sconsigliato.

    Quote Originariamente inviata da shodan Visualizza il messaggio
    Qui parli di pi� observer. Immagino che debbano interagire con i tre (o pi�) subject, oppure ho capito male?
    Qui mi perdo io: in che senso parlo di piu' observer? Se puo servire, io parto dalla necessita' di osservare un oggetto, in alcuni casi due, in modo che le loro modifiche vengano notificate e quindi comportino l'aggiornamento degli osservatori. Per contro, un subject puo' avere piu' di un observer.

    Quote Originariamente inviata da shodan Visualizza il messaggio
    Ce ne sono di migliori (almeno dal punto di vista della OOP), ma per discuterne occorre sapere di quanta roba di un tipo e dell'altro stiamo parlando. Perch� se parliamo di al massimo tre subject, si pu� violare leggermente il pattern. Se parliamo da quattro in su meglio mixare l'obvserver al pattern Visitor.
    Per migliori intendevo migliori modi di individuare il tipo del subject passato tramite il metodo setSubject senza dover ricorrere al dynamic_cast, ma non saprei cosa altro fare.
    Se poi esistono altre strade percorribili, per le mie necessita', se non stravolgono la logica e non sono complicate, conoscere roba nuova puo far solamente bene, altrimenti, essendo programmi per uso personale, non sono alla disperata ricerca di prestazioni o che altro, una volta che funzionano, va bene.

    Grazie!

  10. #10
    Utente di HTML.it L'avatar di shodan
    Registrato dal
    Jun 2001
    Messaggi
    2,381
    In particolare mi interessava sapere se il modo in cui individuo il tipo del subject passato col metodo setSubject e' corretto (uso del dynamic_cast) o si puo' fare altrimenti, usando ad esempio typeid, ma da quanto letto in giro mi pare sconsigliato.
    Si, perché il dynamic_cast<> è abbastanza lento rispetto a una chiamata virtuale, per cui si può "emulare" tramite una enum, una funzione virtuale e uno static_cast<>. Ma non entro nei dettagli perché non è l'oggetto del thread.
    Qui mi perdo io: in che senso parlo di piu' observer?
    Io ho solo riportato quanto hai scritto. Immagino per estendere il pattern.
    Se poi esistono altre strade percorribili, per le mie necessita', se non stravolgono la logica e non sono complicate, conoscere roba nuova puo far solamente bene, altrimenti, essendo programmi per uso personale, non sono alla disperata ricerca di prestazioni o che altro, una volta che funzionano, va bene.
    La strada "accademica" è interessante ma lunga.
    La cosa più semplice è mettere in overload la funzione update() affinché riceva il dato corretto.
    Ti posto un esempio. Nel main puoi vedere come i metodi invocati sono solo quelli dell'interfaccia Observer e Subject.
    Per quanto riguarda lo std::set<>, il suo scopo è solo quello di avere una singola istanza di quanto gli viene passato, cosa che con il vector (di inizio thread) diventa complicato.
    codice:
    class Subject;
    // forward declaration necessarie per i metodi in overload
    class DerSubject1;
    class DerSubject2;
    class DerSubject3;
    
    class Observer {
        public:
            virtual ~Observer() {};
            void setSubject(Subject* sub);
            void removeSubject(Subject* sub);
    
            // update in overload per tipo di subject da ridefinire 
            // nelle derivate da Observer
            virtual void update(DerSubject1* subject) = 0;
            virtual void update(DerSubject2* subject) = 0;
            virtual void update(DerSubject3* subject) = 0;
    
    protected:
            Observer() {};
            std::set<Subject*> mSubjects;
    };
    
    class Subject {
    public:
        virtual ~Subject() {};
        void attach(Observer* observer) {
            mObserverList.insert(observer);
        }
    
        virtual void detach(Observer* observer) {
            mObserverList.erase(observer);
        }
        virtual void notify()= 0;
    protected:
        Subject() {};
        std::set<Observer*> mObserverList;
    };
    
    // occorre metterle qui perché hanno bisogno della definizione delle 
    // funzioni di Subject
    void Observer::setSubject(Subject* sub) {
        sub->attach(this);
        mSubjects.insert(sub);
    }
    
    void Observer::removeSubject(Subject* sub) {
        sub->detach(this);
        mSubjects.insert(sub);
    }
    
    // la funzione notify dev'essere ridefinita nelle singole 
    // derivate da Subject perché dev'essere propagato
    // il tipo di dato reale.
    class DerSubject1 : public Subject {
        public:
            // per l'interfaccia Subjects
            void notify() {
                for( auto it : mObserverList) {
                    it->update(this);
                }
            }
    
            // funzione propria
            std::string getMyName1() { return "DerSubject1"; }
    };
    
    class DerSubject2 : public Subject {
    public:
        // per l'interfaccia Subjects
        void notify() {
            for(auto it : mObserverList) {
                it->update(this);
            }
        }
    
        // funzione propria
        std::string getMyName2() { return "DerSubject2"; }
    };
    
    class DerSubject3 : public Subject {
    public:
        // per l'interfaccia Subjects
        void notify() {
            for(auto it : mObserverList) {
                it->update(this);
            }
        }
    
        // funzione propria
        std::string getMyName3() { return "DerSubject3"; }
    };
    
    class DerObserverMoreSubject : public Observer {
    public:
    
        void update(DerSubject1* subject) override {
            std::cout << "DerObserverMoreSubject observes " << subject->getMyName1() << std::endl;
        };
    
        void update(DerSubject2* subject) override {
            std::cout << "DerObserverMoreSubject observes " << subject->getMyName2() << std::endl;
        }
    
        void update(DerSubject3* subject) override {
            std::cout << "DerObserverMoreSubject observes " << subject->getMyName3() << std::endl;
        }
    };
    
    
    using namespace std;
    
    int main(int argc, char* argv[]) {
    
        DerObserverMoreSubject doms;
        DerSubject1 ds1;
        DerSubject2 ds2;
        DerSubject3 ds3;
    
        Observer* ob = &doms;
        Subject* s1 = &ds1;
        Subject* s2 = &ds2;
        Subject* s3 = &ds3;
    
        ob->setSubject(s1);
        ob->setSubject(s2);
        ob->setSubject(s3);
    
        s1->notify();
        s2->notify();
        s3->notify();
    }
    This code and information is provided "as is" without warranty of any kind, either expressed
    or implied, including but not limited to the implied warranties of merchantability and/or
    fitness for a particular purpose.

Permessi di invio

  • Non puoi inserire discussioni
  • Non puoi inserire repliche
  • Non puoi inserire allegati
  • Non puoi modificare i tuoi messaggi
  •  
Powered by vBulletin® Version 4.2.1
Copyright © 2025 vBulletin Solutions, Inc. All rights reserved.