Herzlich Willkommen, lieber Gast!
  Sie befinden sich hier:

  Forum » C / C++ (ANSI-Standard) » Das Observerpattern

Forum | Hilfe | Team | Links | Impressum | > Suche < | Mitglieder | Registrieren | Einloggen
  Quicklinks: MSDN-Online || STL || clib Reference Grundlagen || Literatur || E-Books || Zubehör || > F.A.Q. < || Downloads   

Autor Thread - Seiten: [ 1 ] > 2 < [ 3 ] [ 4 ]
010
29.08.2006, 22:34 Uhr
kronos
Quotenfisch
(Operator)



Zitat von (un)wissender:
Hm, Kronos, daran habe ich noch gar nicht gedacht. Interessante Idee, allerdings ist das dann mit dem Registrieren/Entfernen vielleicht ein wenig frikelig.
So ist es immerhin ein allgemein und einfach zu verwendener Observer.


Gut, hat beides vor und Nachteile. Im Moment hast du die Flexibilität den selben Observer auf verschiedene Weise notifizieren (tolles Wort) zu können, da ja notify eine Funktion übergeben wird.
Der Nachteil ist, dass die Observer an irgendein gemeinsames Konzept gebunden sein müssen. (Wrapper wickeln geht natürlich immer).
--
main($)??<-$<='?'>>2?main($-!!putchar(
(("$;99M?GD??(??/x0d??/a:???;a"+'?'/4)
??($??)+'?'/3-2-1+$%2)??''?')):'?';??>
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
011
29.08.2006, 23:08 Uhr
(un)wissender
Niveauwart



Zitat von kronos:

Der Nachteil ist, dass die Observer an irgendein gemeinsames Konzept gebunden sein müssen. (Wrapper wickeln geht natürlich immer).



Ja, das stimmt. Ich glaube aber, dass das auch die Idee bei dem Observer-Pattern ist..


Bearbeitung:

Ach ja, die Observer sind jetzt in eine größeres Projekt eingebaut und funktionieren gut. Den Code kann jeder gerne übernehmen, wenn er möchte.


--
Wer früher stirbt ist länger tot.

Dieser Post wurde am 29.08.2006 um 23:10 Uhr von (un)wissender editiert.
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
012
29.08.2006, 23:48 Uhr
(un)wissender
Niveauwart


Habe noch einiges optimiert.
Also weil so schön ist, ein letzes Mal
--
Wer früher stirbt ist länger tot.
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
013
29.08.2006, 23:49 Uhr
(un)wissender
Niveauwart


Observable.hpp


C++:
#ifndef OBS_OBSERVABLE
#define OBS_OBSERVABLE

#include <vector>
#include <set>

namespace obs
{
    //Implementiert das Observerpattern, (hohe) exception-Sicherheit,
    //nicht thread-sicher.
    //
    //Bemerkung: hinzuzufügend meint registerObserver,
    //    zuentfernend unregisterObserver.
    //
    //Es können während eines notify-Vorgangs Observer
    //hinzugefügt oder entfernt werden.
    //Jeder Observer kann nur einmal hinzugefügt werden.
    //Bei Verletzung dieser Regel wird eine
    //std::logic_error-Ausnahme ausgelöst.
    //
    //Für notify gilt:
    //    allowRecursiveNotify == true ->
    //        Benachrichtigte Observer können einen erneuten
    //        notify-Vorgang auslösen.
    //        Pro notify-Vorgang kann nur eine Wiederholung gespeichert werden.
    //        Der aktuelle Vorgang wird erst komplett zu Ende gebracht.
    //        Eventuell hinzuzufügende bzw. zu entfernende Observer
    //        werden vor dem erneuten notify-Vorgang inzugefügt bzw. entfernt.
    //        Tritt während eines notify-Vorgangs eine Ausnahme auf,
    //        wird der Vorgang abgebrochen und eine eventuelle Wiederholung
    //        wird nicht durchgeführt.
    //        Hinzuzufügende bzw. zu entfernende Observer werden
    //        hinzugefügt bzw. entfernt.
    //        ACHTUNG: Die Observer haben dafür zu sorgen,
    //        dass es keine Endlosrekursion von notify gibt!
    //    allowRecursiveNotify == false ->
    //        Wird während eines notify-Vorgangs notify erneut aufgerufen,
    //        wird eine std::runtime_error-Ausnahme ausgelöst.
    //        Hinzuzufügende bzw. zu entfernende Observer werden
    //        hinzugefügt bzw. entfernt.
    //    Es kann passieren, dass die während eines notify-Vorgangs
    //    zwischen gespeicherten hinzuzufügenden und zuentfernenden Observer
    //    nicht alle hinzugefügt bzw. entfernt werden.
    //  Der Grund hierfür ist das Auslösen einer Ausnahme,
    //     bspw. durch das Kopieren der Observer oder bei Speichermangel.
    //
    // Stefan Andreßen, den 29. August 2006
    //
    template<class T>
    class Observable
    {
    public:
        Observable();
        virtual ~Observable();
        void registerObserver(T const& observer);
        void unregisterObserver(T const& observer);
        template<class U> void notify(U const& u,
            bool allowRecursiveNotify = false);
        bool hasObserver(T const& observer) const;

    private:
        template<class Y, class X>
        void makeObserverUpdater(Y & toUpdate, X const& x);

        std::set<T>    _observer,
                    _observerToErase,
                    _observerToInsert;
        bool        _notify,
                    _recursive;
    };
}

#include "Observable.cpp"

#endif


--
Wer früher stirbt ist länger tot.
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
014
29.08.2006, 23:50 Uhr
(un)wissender
Niveauwart


Observable.cpp


C++:
#include <boost/bind.hpp>
#include <stdexcept>
#include <algorithm>
#include <utility>

#include <iostream>

namespace obs
{
    namespace
    {
        template<bool init, bool exit>
        class BoolGuard
        {
        public:
            inline BoolGuard(bool & mybool);              
            inline ~BoolGuard();
            inline bool isBoolValue() const;
            inline void setBoolValue(bool value);

        private:
             bool & _bool;
        };
        
        template<class T, class V>
        class ObserverUpdater
        {
        public:
            inline ObserverUpdater(T &observerToUpdate,
                V const& updater);
            inline ~ObserverUpdater();                                            
        
        private:
             T & _observerToUpdate;
             V const& _updater;
        };    
    }

    template<class T>
    Observable<T>::Observable()
        : _notify(false)
    {
    }

    template<class T>
    Observable<T>::~Observable()
    {
    }

    template<class T>
    void Observable<T>::registerObserver(T const& observer)
    {
        std::pair<std::set<T>::iterator, bool> res;
        if(_notify)
        {
            //Nur einmal merken!
            res = _observerToInsert.insert(observer);    
            //Wenn vorhanden, das wieder löschen verhindern.
            if(res.second)
            {
                _observerToErase.erase(observer);
            }
        }
        else
        {
            res = _observer.insert(observer);        
        }
        if(!res.second)
        {
            throw
                std::logic_error(
                    "Observer ist already registered.");
        }
    }

    template<class T>
    void Observable<T>::unregisterObserver(T const& observer)
    {        
        if(_notify)
        {
            //Nur einmal merken!
            _observerToErase.insert(observer);
            //Wenn vorhanden, das wieder einfügen verhindern.
            _observerToInsert.erase(observer);
        }
        else
        {
            _observer.erase(observer);
        }
    }
    
    template<class T>
        template<class U>
    void Observable<T>::notify(U const& u, bool allowRecursiveNotify)
    {
        if(_notify)
        {    
            if(!allowRecursiveNotify)
            {
                throw
                    std::runtime_error(
                        "runtime_error: Recursive call of notify.");
            }
            else
            {
                _recursive = true;
                return;
            }
        }
        
        BoolGuard<false, false> recursiveGuard(_recursive);
        BoolGuard<true, false> notifyGuard(_notify);

        std::for_each(_observer.begin(), _observer.end(), u);
        
        typedef std::set<TestObserver *>::size_type
            (std::set<TestObserver *>::*Eraser)
                (typename std::set<TestObserver *>::key_type const&);
        Eraser eraser = &std::set<T>::erase;
        makeObserverUpdater(_observerToErase,
            boost::bind(eraser, &_observer, _1));

        typedef std::pair<typename std::set<T>::iterator, bool>
            (std::set<TestObserver *>::*Inserter)
                (typename std::set<TestObserver *>::key_type const&);
        Inserter inserter = &std::set<T>::insert;
        makeObserverUpdater(_observerToInsert,
            boost::bind(inserter, &_observer, _1));  
        
        if(recursiveGuard.isBoolValue())
        {
            notifyGuard.setBoolValue(false);
            if(!allowRecursiveNotify)
            {
                throw
                    std::runtime_error(
                        "runtime_error: Recursive call of notify.");
            }
            notify(u, allowRecursiveNotify);
        }
    }
    
    template<class T>
    bool Observable<T>::hasObserver(T const& observer) const
    {
        bool found = _observer.end() != _observer.find(observer);
        if(_notify)
        {
            return (found &&
                    //vorhanden und nicht zu löschen
                    //und nicht hinzuzufügen
                    _observerToErase.end() ==
                    _observerToErase.find(observer))                    
                    ||
                    (!found &&
                    //nicht gefunden, wird ab hinzugefügt
                    _observerToInsert.end() !=
                    _observerToInsert.find(observer));
        }                
        return found;
    }

    template<class T>
        template<class Y, class X>
    void Observable<T>::makeObserverUpdater(Y & toUpdate,
        X const& x)
    {
        ObserverUpdater<Y, X>(toUpdate, x);
    }

    namespace
    {
        template<bool init, bool exit>
        BoolGuard<init, exit>::BoolGuard(bool & mybool)
            : _bool(mybool)
        {
             _bool = init;
        }

        template<bool init, bool exit>
        BoolGuard<init, exit>::~BoolGuard()
        {            
            _bool = exit;
        }

        template<bool init, bool exit>
        bool BoolGuard<init, exit>::isBoolValue() const
        {
            return _bool;
        }

        template<bool init, bool exit>
        void BoolGuard<init, exit>::setBoolValue(bool value)
        {
            _bool = value;
        }

        template<class T, class V>
        ObserverUpdater<T, V>::ObserverUpdater(
            T &observerToUpdate,
            V const& updater)
            : _observerToUpdate(observerToUpdate), _updater(updater)
        {
        }

        template<class T, class V>
        ObserverUpdater<T, V>::~ObserverUpdater()
        {
            try
            {        
                std::for_each(_observerToUpdate.begin(),
                    _observerToUpdate.end(), _updater);  
                _observerToUpdate.clear();
            }
            catch(...)
            {
                _observerToUpdate.clear();
            }
        }
    }
};


--
Wer früher stirbt ist länger tot.
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
015
30.08.2006, 08:21 Uhr
Spacelord
Hoffnungsloser Fall


Moin,
nichts für ungut aber ich halte die Lösung für oversized.
Insbesondere kann ich nicht erkennen wo du durch den Einsatz von templates an Flexibilität gewinnst.Im Gegenteil....

C++:
bs.notify(
            boost::bind(&TestObserver::observableChanges,
            _1, "los"),
            true);


.....
class Obsv
    : public obs::Observable<TestObserver *>
{
};



In dem Fall würde man imho mit 2 Basisklassen Observable und Observer besser fahren.
Das Observable Objekt soll ja schliesslich von beliebigen Klassen die das Observer Interface implementieren(ne update, valueChanged oder weiß der Herr wie man die Methode in Observer nennt) "überwacht" werden können.

Gruss Spacelord
--
.....Ich mach jetzt nämlich mein Jodeldiplom.Dann hab ich endlich was Eigenes.

Dieser Post wurde am 30.08.2006 um 08:23 Uhr von Spacelord editiert.
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
016
30.08.2006, 09:15 Uhr
(un)wissender
Niveauwart


Na ja, Obsv kann jetzt z.B. eigene notify-Methoden entwickeln, die sehr einfach gestrickt sind und auch das boost::bind kapseln. Was aber eh sehr einfach zu benutzen ist (in den meisten Fällen).

Ich habe die Erfahrung gemacht, dass eine allgemeine Observerschnittstelle sehr schnell dafür sorgt, dass die Observer zu generell werden und wieder viel Code zu Unterscheidung der Fälle geschrieben werden muss (langsam und schwer zu warten). Das habe ich jetzt in zwei Projekten erlebt.

Aus diesem Grund gibt es keine Observerschnittstelle, bzw. jede Klasse kann eine Schnittstelle sein.

Der Einsatz des Observercodes in meinem Projekt bestätigt bisher die gute Wartbarkeit und das einfache Interface.
--
Wer früher stirbt ist länger tot.
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
017
30.08.2006, 10:55 Uhr
(un)wissender
Niveauwart


Die aktuelle Version kann hier runtergeladen werden.
--
Wer früher stirbt ist länger tot.

Dieser Post wurde am 30.08.2006 um 10:56 Uhr von (un)wissender editiert.
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
018
30.08.2006, 11:51 Uhr
stephanw
localhorst


Ich habe mr den Code nicht wirklich angesehen... aber könnte man Observable nicht in Subject umbenennen ? Ein namespace "obs" könnte dann "SubjectObserver" heißen.
--
Reden ist Schweigen und Silber ist Gold.
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
019
30.08.2006, 12:27 Uhr
(un)wissender
Niveauwart


Namen sind Schall und Rauch.
Aber vielleicht wäre es intuitiver mit Subject.
Ich hatte mich an Java erinnert, da heißt das auch Observable.
--
Wer früher stirbt ist länger tot.
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
Seiten: [ 1 ] > 2 < [ 3 ] [ 4 ]     [ C / C++ (ANSI-Standard) ]  


ThWBoard 2.73 FloSoft-Edition
© by Paul Baecher & Felix Gonschorek (www.thwboard.de)

Anpassungen des Forums
© by Flo-Soft (www.flo-soft.de)

Sie sind Besucher: