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 ]
000
29.08.2006, 15:35 Uhr
(un)wissender
Niveauwart


Im folgendem möchte ich euch etwas Code von mir vorstellen. Kritik und Kommentare sind erwünscht. Lest am besten den Kommentar im Header um zu wissen was die Klasse tut.
--
Wer früher stirbt ist länger tot.
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
001
29.08.2006, 15:36 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
    //        hinzugefü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 u, bool allowRecursiveNotify = false);
        bool hasObserver(T const& observer) const;

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

        std::set<T>        _observer;
        std::vector<T>  _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
002
29.08.2006, 15:39 Uhr
(un)wissender
Niveauwart


Observable.cpp


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

#include <iostream>

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

        private:
             bool & _bool;
             bool _exit;
        };
        
        template<class T, class V>
        class ObserverUpdater
        {
        public:
            inline ObserverUpdater(std::vector<T> &observerToUpdate,
                V const& updater);
            inline ~ObserverUpdater();                                            
        
        private:
             std::vector<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)
    {
        if(hasObserver(observer))
        {
            throw std::logic_error("Observer ist already registered.");
        }
        if(_notify)
        {
            if(_observerToInsert.end() ==
                std::find(_observerToInsert.begin(),
                    _observerToInsert.end(), observer))
            {
                _observerToInsert.push_back(observer); //Nur einmal merken!
            }
            //Wenn vorhanden, das wieder löschen verhindern.
            _observerToErase.erase(
                std::remove(_observerToErase.begin(),
                    _observerToErase.end(), observer),
                _observerToErase.end());
        }
        else
        {
            _observer.insert(observer);
        }
    }

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

        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() ==
                        std::find(_observerToErase.begin(),
                            _observerToErase.end(), observer))
                    ||
                    (!found &&
                    //nicht gefunden, wird ab hinzugefügt
                        _observerToInsert.end() !=
                        std::find(_observerToInsert.begin(),
                            _observerToInsert.end(), observer));
        }                
        return found;
    }

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

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

        BoolGuard::~BoolGuard()
        {            
            _bool = _exit;
        }

        bool BoolGuard::isBoolValue() const
        {
            return _bool;
        }

        void BoolGuard::setBoolValue(bool value)
        {
            _bool = value;
        }

        template<class T, class V>
        ObserverUpdater<T, V>::ObserverUpdater(
            std::vector<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
003
29.08.2006, 15:40 Uhr
(un)wissender
Niveauwart


main.cpp (Bsp. und test)


C++:
#include <functional>
#include <algorithm>
#include <iostream>
#include <iterator>
#include <string>
#include "Observable.hpp"

class TestObserver;

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

namespace
{
    Obsv bs;
}

class TestObserver
{
public:
    TestObserver(std::string const &name)
        : _name(name), _notifyCalled(false)
    {
    }

    void observableChanges(std::string const& message)
    {
        std::cout << _name << ": " << message << '\n';
        if(!_notifyCalled)
        {
            bs.notify(
                boost::bind(&TestObserver::observableChanges,
                _1, "los"),
                true);
            bs.unregisterObserver(this);
            bs.registerObserver(this);
            _notifyCalled = true;
        }    
    }  

    bool operator==(TestObserver const &obs) const
    {
        return this == &obs;
    }

    void setNotifyCalled(bool value)
    {
        _notifyCalled = value;
    }

private:
    std::string const _name;
    bool _notifyCalled;
};

int main()
{
    std::cout << sizeof(Obsv) << '\n';
    try
    {
        TestObserver t1("t1"), t2("t2"), t3("t3");
        bs.registerObserver(&t1);
        bs.registerObserver(&t2);
        bs.registerObserver(&t3);
        bs.notify(
            boost::bind(&TestObserver::observableChanges,
            _1, "los"),
            true);
        bs.unregisterObserver(&t1);
        t2.setNotifyCalled(false);
        bs.notify(
            boost::bind(&TestObserver::observableChanges,
            _1, "los"),
            false);
    }
    catch(std::exception const& ex)
    {
        std::cout << ex.what();
    }        
}


--
Wer früher stirbt ist länger tot.
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
004
29.08.2006, 15:46 Uhr
FloSoft
Medialer Over-Flow
(Administrator)


was bringt das eigentlich?
--
class God : public ChuckNorris { };
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
005
29.08.2006, 15:55 Uhr
ao

(Operator)


guckst du hier:

http://de.wikipedia.org/wiki/Beobachter_%28Entwurfsmuster%29
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
006
29.08.2006, 16:33 Uhr
FloSoft
Medialer Over-Flow
(Administrator)


aso ja klar jetzt wirds klarer was das zeuch tut
--
class God : public ChuckNorris { };
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
007
29.08.2006, 17:38 Uhr
kronos
Quotenfisch
(Operator)


Hm, sieht schon flashig aus :-)
Meine Diletanten-Meinung auf den ersten Blick:
- Könntest du statt observer-Objekten nicht einfach Funktionen sammeln, die bei einem notify aufgerufen werden?
- ich weiß nicht inwieweit der BoolGuard nachher wegoptimiert wird, aber evtl. würde es sich lohnen init und exit als template-Argumente zu übergeben...
--
main($)??<-$<='?'>>2?main($-!!putchar(
(("$;99M?GD??(??/x0d??/a:???;a"+'?'/4)
??($??)+'?'/3-2-1+$%2)??''?')):'?';??>

Dieser Post wurde am 29.08.2006 um 17:44 Uhr von kronos editiert.
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
008
29.08.2006, 21:53 Uhr
(un)wissender
Niveauwart



Zitat von kronos:

-könntest du statt observer-Objekten nicht einfach Funktionen sammeln, die bei einem notify aufgerufen werden?



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.


Zitat von kronos:

-ich weiß nicht inwieweit der BoolGuard nachher wegoptimiert wird, aber evtl. würde es sich lohnen init und exit als template-Argumente zu übergeben...



Ja, stimmt, das geht auf jeden Fall. Sollte eigentlich auch so wegoptimiert werden, aber als template-Argumente wäre es wohl besser.
--
Wer früher stirbt ist länger tot.
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
009
29.08.2006, 22:04 Uhr
(un)wissender
Niveauwart


Habe kronos Anmerkung mit den template-Parametern eingefügt.


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(std::vector<T> &observerToUpdate,
                V const& updater);
            inline ~ObserverUpdater();                                            
        
        private:
             std::vector<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)
    {
        if(hasObserver(observer))
        {
            throw std::logic_error("Observer ist already registered.");
        }
        if(_notify)
        {
            if(_observerToInsert.end() ==
                std::find(_observerToInsert.begin(),
                    _observerToInsert.end(), observer))
            {
                _observerToInsert.push_back(observer); //Nur einmal merken!
            }
            //Wenn vorhanden, das wieder löschen verhindern.
            _observerToErase.erase(
                std::remove(_observerToErase.begin(),
                    _observerToErase.end(), observer),
                _observerToErase.end());
        }
        else
        {
            _observer.insert(observer);
        }
    }

    template<class T>
    void Observable<T>::unregisterObserver(T const& observer)
    {
        if(_notify)
        {
            if(_observerToErase.end() ==
                std::find(_observerToErase.begin(),
                    _observerToErase.end(), observer))
            {
                _observerToErase.push_back(observer); //Nur einmal merken!
            }
            //Wenn vorhanden, das wieder einfügen verhindern.
            _observerToInsert.erase(
                std::remove(_observerToInsert.begin(),
                    _observerToInsert.end(), observer),
                _observerToInsert.end());
        }
        else
        {
            _observer.erase(observer);
        }
    }
    
    template<class T>
        template<class U>
    void Observable<T>::notify(U 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() ==
                        std::find(_observerToErase.begin(),
                            _observerToErase.end(), observer))
                    ||
                    (!found &&
                    //nicht gefunden, wird ab hinzugefügt
                        _observerToInsert.end() !=
                        std::find(_observerToInsert.begin(),
                            _observerToInsert.end(), observer));
        }                
        return found;
    }

    template<class T>
        template<class X>
    void Observable<T>::makeObserverUpdater(std::vector<T> & toUpdate,
        X const& x)
    {
        ObserverUpdater<T, 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(
            std::vector<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
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: