[.NET] Flexibel state patten

Pagina: 1
Acties:

Acties:
  • 0 Henk 'm!

  • armageddon_2k1
  • Registratie: September 2001
  • Laatst online: 07-07 19:35
Uitleg
Mijn vriendin promoveert en aangezien ik haar graag help met enkele computerdingen daaromtrent programmeer ik dus ook af en toe experimenten voor haar. Leerzaam, leuk en voor haar makkelijk omdat het makkelijk itereren is.

Experimenten van haar bestaan vaak uit een lading stimuli waar op gereageerd moet worden. Al naar gelang de respons hierop moet er iets anders gebeuren. Deze sequentie noem ik gemakshalve het protocol.

Alle stimuli zijn gelukkig simpel gedefinieerd en staan als csv in een file. Met behulp van een csv-library lees ik deze in en zet ik hem om in het model, welke bestaat uit een paar simpele C# classes.
Normaal gesproken programmeerde ik wat logica en maakte ik een nieuwe .exe voor haar en kon ze beginnen.

Protocollen worden complexer en mijn code steeds meer spaghetti. Tijd voor een goede refactorsessie :)

Probleem
Bij een protocol denk ik meteen aan een state-pattern en deze werkt dus ook goed in de praktijk. Ik gebruik simpele classes die allemaal een Interface implementeren. Als ze aangemaakt worden wordt de onEntry aangeroepen, als ze verlaten worden de onExit, en bij interactie een andere methode. De context van de states bevat alle nodige informatie om beslissingen te maken in die methodes. Omdat het relatief weinig states zijn is het snel opgezet. Echter voor elk protocol verschilt het Entry en Exit gedrag en het gedrag onder invloed van interacties.

Ik breek me al een tijdje het hoofd hoe ik dit netjes op kan lossen zonder al te veel te herhalen van code, mezelf in de toekomst in de voet te schieten en flexibel te zijn.

Beoogde oplossing
Ik kan de Entry, Exit en interactie methodes natuurlijk als delegate definieren (OnEntry, OnExit, OnReact ... etc) en dan in een protocol-klasse methodes linken aan deze delegates. Het is echter zo dat bij een state-change er een nieuwe instance gemaakt wordt van een state en de delegates dus verloren gaan. Dit kan ik natuurlijk oplossen door een static delegate te doen, maar dat voelt zo vies dat ik bang ben dat als er meer complexiteit bij komt kijken ik weer de sjaak ben.

Dus...
Aan jullie de vraag of er een elegante oplossing is om in het state-pattern de Entry, Exit en interactie methodes te variëren al naar gelang het protocol verandert.

Disclaimer:
Er bestaat open-source software hiervoor, zoals OpenSesame, maar ook betaalde software, zoals E-Prime. De protocollen zijn met zulke pakketten best te bouwen, maar kost onevenredig veel tijd als het wat complexer wordt en ik vind het leuk om dit uit te pluizen :). Bovenstaande pakketten zijn een fall-back mocht het allemaal niet lukken ;) Ik moet niet een hindernis gaan zijn voor het promotiewerk van mijn vriendin.

Engineering is like Tetris. Succes disappears and errors accumulate.


Acties:
  • 0 Henk 'm!

  • H!GHGuY
  • Registratie: December 2002
  • Niet online

H!GHGuY

Try and take over the world...

Als je van een uitdaging houdt, kun je kijken of code genereren voor boost MSM werkt:
http://www.boost.org/doc/...s/msm/doc/HTML/index.html

Deze kun je dan automatisch compileren en uitvoeren.

--

Waarom maak je trouwens telkens nieuwe state objecten aan?
Waarschijnlijk kom je al een heel eind als je dat eruit haalt.

[ Voor 24% gewijzigd door H!GHGuY op 02-02-2013 12:48 ]

ASSUME makes an ASS out of U and ME


Acties:
  • 0 Henk 'm!

  • cenix
  • Registratie: September 2001
  • Laatst online: 09-07 19:07
Kijk ook eens naar State Machine Compiler, http://smc.sourceforge.net/
Ik weet niet alleen niet hoe goed de C#/.NET support is. SMC gebruikt het state pattern uit GoF.

Acties:
  • 0 Henk 'm!

  • Zoijar
  • Registratie: September 2001
  • Niet online

Zoijar

Because he doesn't row...

Ik snap het niet helemaal exact, dus ik gok even wat, maar als ik het een beetje begrijp wil dus eigenlijk functionaliteit uitvoeren op basis van twee (dynamische) object typen: het huidige protocol, en de huidige state? Kom je dan niet uit op double dispatch, en in C++ dus iets als Visitor?

(ik zie iets voor me als een subclass van je context voor elk protocol, die weer wordt aangeroepen door je states. Dus je context voert op de huidige state een functie onEntry() uit, die state beslist wat dingen, en voor protocol specifieke operaties roept de state weer de (abstracte) context aan die dan overloaded is door een concrete protocol)

[ Voor 39% gewijzigd door Zoijar op 02-02-2013 15:49 ]


Acties:
  • 0 Henk 'm!

  • armageddon_2k1
  • Registratie: September 2001
  • Laatst online: 07-07 19:35
@HighGuy, Cenix: Bedankt voor de links! Ik zal er eens naar kijken.

@Zoijar: Je hebt volgens mij gelijk. Dat klinkt inderdaad zoals ik bedoel. Alhoewel het ik het moeilijk vind zulke abstract dingen goed op te schrijven, danwel te lezen. Wat jij zegt snijdt wel hout, dus daar ga ik eens naar kijken.

Engineering is like Tetris. Succes disappears and errors accumulate.


Acties:
  • 0 Henk 'm!

  • Zoijar
  • Registratie: September 2001
  • Niet online

Zoijar

Because he doesn't row...

Zoiets had ik in m'n hoofd, maar ik weet niet of dat je feitelijke probleem op lost:

C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Protocol {
    State* state;

   void setState(State* s) {state = s;}
   void onEntry() {state->onEntry(this);}

virtual State* nextState(State*) {return nullptr;}
virtual State* nextState(StateA*) = 0;
virtual State* nextState(StateB*) = 0;
... //etc
};

class MyProtocol : public Protocol {
virtual State* nextState(StateA*) {return new StateB;}
};

class State {
   virtual void onEntry(Protocol*) = 0;
};

class StateA : public State {
   void onEntry(Protocol* p) {// do stuff. p->setState(p->nextState(this));}
};


oid. Of dit goed is weet ik niet; misschien kan je er iets mee. Grote nadeel van dit soort dingen is als je een state toevoegt je al je protocols moet aanpassen... tenzij je een redelijke default hebt (wat nu nullptr returned)

[ Voor 10% gewijzigd door Zoijar op 02-02-2013 17:08 ]


Acties:
  • 0 Henk 'm!

  • armageddon_2k1
  • Registratie: September 2001
  • Laatst online: 07-07 19:35
Ik heb een default set states. Ik heb mijn Design Patterns boekje van de GoF er even bijgepakt en het Visitor pattern lijkt er goed op. Bedankt voor je hulp, ik zal hier eens goed naar kijken.

Engineering is like Tetris. Succes disappears and errors accumulate.


Acties:
  • 0 Henk 'm!

  • armageddon_2k1
  • Registratie: September 2001
  • Laatst online: 07-07 19:35
H!GHGuY schreef op zaterdag 02 februari 2013 @ 09:31:
Waarom maak je trouwens telkens nieuwe state objecten aan?
Waarschijnlijk kom je al een heel eind als je dat eruit haalt.
Je hebt gelijk. Dat is inderdaad iets wat ik nog niet beseft had. In de voorbeelden die ik zag op internet maakte men altijd de state opnieuw aan. Het kan geen kwaad de states in de Context alvast te instantieren. Dat scheelt al een hoop.

Engineering is like Tetris. Succes disappears and errors accumulate.


Acties:
  • 0 Henk 'm!

  • Zoijar
  • Registratie: September 2001
  • Niet online

Zoijar

Because he doesn't row...

armageddon_2k1 schreef op zaterdag 02 februari 2013 @ 17:16:
Je hebt gelijk. Dat is inderdaad iets wat ik nog niet beseft had. In de voorbeelden die ik zag op internet maakte men altijd de state opnieuw aan. Het kan geen kwaad de states in de Context alvast te instantieren. Dat scheelt al een hoop.
States zijn ook vaak singletons; hoewel ik zelf licht allergisch ben voor singletons -- zou ze liever gewoon een keer initialiseren idd.

Acties:
  • 0 Henk 'm!

  • armageddon_2k1
  • Registratie: September 2001
  • Laatst online: 07-07 19:35
Ik vind singletons soms nuttig 'lijken', maar elke keer als ik ze gebruik schiet ik mezelf op gegeven moment in de voet. Ik heb nog nooit een goed argument voor een singleton gezien die me overtuigde het te gebruiken.

Engineering is like Tetris. Succes disappears and errors accumulate.


Acties:
  • 0 Henk 'm!

  • armageddon_2k1
  • Registratie: September 2001
  • Laatst online: 07-07 19:35
Even een update:
Ik heb het inderdaad middels een Visitor pattern opgelost. Het is uiteindelijk een redelijk simpele set aan classes geworden, maar het belangrijkste was wel bedenken wat waarvoor precies verantwoordelijk was.

De states zijn verantwoordelijk voor de vertaling van inputs naar veranderingen in het model.
De protocollen zijn verantwoordelijk voor de transities van states, en al naar gelang het type protocol en het model wordt een state geladen.

Toen ik dat heel erg duidelijk had was het eigenlijk best simpel :)

Engineering is like Tetris. Succes disappears and errors accumulate.


Acties:
  • 0 Henk 'm!

  • Zoijar
  • Registratie: September 2001
  • Niet online

Zoijar

Because he doesn't row...

All problems in computer science can be solved by another level of indirection ;)
Pagina: 1