Du bist nicht angemeldet.

Stilllegung des Forums
Das Forum wurde am 05.06.2023 nach über 20 Jahren stillgelegt (weitere Informationen und ein kleiner Rückblick).
Registrierungen, Anmeldungen und Postings sind nicht mehr möglich. Öffentliche Inhalte sind weiterhin zugänglich.
Das Team von spieleprogrammierer.de bedankt sich bei der Community für die vielen schönen Jahre.
Wenn du eine deutschsprachige Spieleentwickler-Community suchst, schau doch mal im Discord und auf ZFX vorbei!

Werbeanzeige

1

09.08.2012, 02:16

StateManager

Da ich in meinem derzeitigen Projekt viel auf das State Pattern zurückgreife und damit ständig immer wieder den selben Code schreibe, habe ich mich mal an eine generalisierende Form gemacht.

Ich wollte einfach mal wissen, was ihr davon haltet.

Bisher drin ist:
Selbstverständlich die Auswahl einer belieben Basisklasse aller States.
Die Auswahl eines beliebigen Keys, mit dem der State gewechselt werden soll. Eingebaute Typen sind aber zu bevorzugen, da bisher alles per Value kopiert wird.
Die Möglichkeit, entweder einfach nur einen aktuellen State zu speichern oder einen Stack zu benutzen.
Die Möglichkeit, alle States vorher zu erstellen oder just in time, wobei eine eigene Funktion oder ein eigener Functor bereit gestellt werden muss.
Das Wechseln des States geschieht über boost::function (das pop für den Stack eingeschlossen), es ist also kein Problem, das Wechseln an beliebigen Orten der Anwendung geschehen zu lassen.
Zugreifen auf den aktullen State über einen getter.
Ein Ausgangszustand muss den Konstruktor übergeben werden.

Was ich noch so plane.
Fehlerbehandlung (ungültiger Key, Stack leer, usw..) mit verschiedenen Optionen mittels Templates.
(heute abend bin ich dazu zu faul :P )
Copy per reference für benutzerdefinierte Typen (sollte wohl mit einer Typelist gehen)
Member Funktionen des aktullen States dem StateManager als Zeiger übergeben und dann ausführen lassen, so dass der gefährliche Getter unnötig wird.
Die Möglichkeit, (de-)initialisierungsfunktionen automatisch beim wechseln des States ausführen zu lassen.
Auf dieses

C-/C++-Quelltext

1
SwitchState<State, States, StateStore, NoStack> startWaiting = stateMgr.getSwitchState (waiting);

nervige Getippe verzichten zu können, soweit möglich.

Hier der Code:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
#include <map>
#include <stack>
#include <boost\function.hpp>
#include <boost\shared_ptr.hpp>


template <class State, class Key>
class StateStore
{
    public:
        void registerState (boost::shared_ptr<State> state, Key key)
        {   
            mStates.insert (std::make_pair (key, state) );
        }
        
        boost::shared_ptr<State> getState (Key key)
        {
            return mStates [key];
        }
    private:    
        std::map<Key, boost::shared_ptr<State>> mStates;
};


template <class State, class Key>
class StateCreator
{
    public:
        void registerState (boost::function<boost::shared_ptr<State> ()> creator, Key key)
        {
            mCreators.insert (std::make_pair (key, creator) );
        }
        
        boost::shared_ptr<State> getState (Key key)
        {
            return mCreators [key] ();
        }

    private:    
        std::map<Key, boost::function<boost::shared_ptr<State> ()>> mCreators;
};





template <class State>
class NoStack
{
    public:
        NoStack (boost::shared_ptr<State> initialState)
            : mCurrentState (initialState)
        {
        }
        
        void switchState (boost::shared_ptr<State> state)
        {   
            mCurrentState = state;
        }
        
        boost::shared_ptr<State> useCurrentState () const
        {
            return mCurrentState;
        }
        
    private:
        boost::shared_ptr<State> mCurrentState;
};


template <class State>
class PopState;

template <class State>
class StateStack 
{
    public: 
        StateStack (boost::shared_ptr<State> initialState)
        {
            mStack.push (initialState);
        }
        
        void switchState (boost::shared_ptr<State> state)
        {
            mStack.push (state);
        }
        
        boost::shared_ptr<State> useCurrentState () const
        {
            return mStack.top ();
        }
        
        void popState ()
        {
            mStack.pop ();
        }
        
        PopState<State> getPopState ();
        
    private:
        std::stack<boost::shared_ptr<State>> mStack;
};


template <class State>
class PopState 
{
    public:
        PopState (StateStack<State>* stateStack)
         : mStateStack (stateStack)
     {
     }
     
     void operator () ()
     {
        mStateStack->popState ();
     }
     
    private:
        StateStack<State>* mStateStack;
};


template <class State>
PopState<State> StateStack<State>::getPopState ()
{
    return PopState<State> (this);
}


template <class State, class Key, template <class, class> class StateRegister,
          template <class> class UseStack>
class SwitchState;


template <class State, class Key, template <class, class> class StateRegister,
         template <class> class UseStack>
class StateManager : public StateRegister<State, Key>, public UseStack<State> 
{
    public: 
        StateManager (boost::shared_ptr<State> initialState)
            : UseStack<State> (initialState)
        {
        }

        SwitchState<State, Key, StateRegister, UseStack> getSwitchState (Key key);
        
    private:
};


template <class State, class Key, template <class, class> class StateRegister,
         template <class> class UseStack>
class SwitchState
{
    public:
        SwitchState (StateManager<State, Key, StateRegister, UseStack>* stateMgr, Key key)
            : mStateMgr (stateMgr), mKey (key)
        {
        }
        
        void operator () ()
        {
            mStateMgr->switchState (mStateMgr->getState (mKey) );
        }
        
    private:
        StateManager<State, Key, StateRegister, UseStack>* mStateMgr;
        Key mKey;
};


template <class State, class Key, template <class, class> class StateRegister,
         template <class> class UseStack>
SwitchState<State, Key, StateRegister, UseStack> StateManager<State, Key, StateRegister, UseStack>::getSwitchState (Key key)
{
    return SwitchState<State, Key, StateRegister, UseStack> (this, key);
}


Wie gesagt, Meinungen, Vorschläge, etc. .. sind erwünscht :)

BlueCobold

Community-Fossil

Beiträge: 10 738

Beruf: Teamleiter Mobile Applikationen & Senior Software Engineer

  • Private Nachricht senden

2

09.08.2012, 06:59

Irgendwie hätte ich von einem Manager erwartet, dass er die Übergänge kontrolliert und steuert, aber irgendwie ist er ja doch nur ein Container, der States speichert (get, set und insert-Methoden) und dem extern gesagt werden muss, welcher State wann aktiv wird. Braucht man da nicht extern noch ziemlich viel andere Logik, damit man mit dem Ding was anfangen kann?

Spätestens bei template <class State, class Key, template <class, class> class StateRegister, template <class> class UseStack> kommt in mir auch das Gefühl hoch, dass da irgendwas nicht ganz richtig ist. Ich hätte da irgendwie Interface-Typen erwartet, aber keine lange Latte von template-Parametern. Ich kann mir kaum vorstellen, dass die tatsächlich notwendig sind und sie stören den Lesefluss schon irgendwie.
Ein SwitchState (Ist es wirklich ein SwitchState oder ist es ein StateSwitch?) braucht einen State, Key, StateRegister und UseStack als generische Parameter? Doch eher nicht, oder? Die reichen doch prima im Konstruktor. Und die Sache mit den Keys finde ich auch ungünstig, ich denke, dass man bei all dem da überhaupt keine Keys braucht, die müsste man sich ja auch wieder extern merken müssen. Also entweder sollten sie eine Eigenschaft des States sein oder es gibt keine.
Du willst ja eigentlich auf "nerviges Getippe" verzichten und ich denke, dass da "SwitchState<State, States, StateStore, NoStack>" eher kontraproduktiv ist. Vor allem interessiert es den SwitchState überhaupt nicht, welcher Typ "StateStore" und "NoStack" sind. Es ist unnötig zu wissen und kann weg. Lieber mit einem Interface-Typ arbeiten an dieser Stelle und die Implementierung nach außen gar nicht preisgeben.
Teamleiter von Rickety Racquet (ehemals das "Foren-Projekt") und von Marble Theory

Willkommen auf SPPRO, auch dir wird man zu Unity oder zur Unreal-Engine raten, ganz bestimmt.[/Sarkasmus]

Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von »BlueCobold« (09.08.2012, 07:37)