<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[[Design] State Pattern, Zustände mit Thread]]></title><description><![CDATA[<p>Hi,</p>
<p>mal wieder ein Problem, was mir im Kopf rumschwirrt und wo ich andere Meinungen brauche:</p>
<p>Ausgangslage: Ich habe eine Klasse (Abbildung eines Device, davon gibt es viele Objekte), welche mehrere Funktionen unterstützt, die je nach Zustand der Klasse allerdings nicht immer alle verfügbar sind. Nach aussen hin soll diese Abhängigkeit bei der Benutzung nicht ersichtlich sein (und nicht möglich). Befehle, die im falschen Zustand ausgeführt werden sollen, kann man zwischenpuffern.</p>
<p>Das folgende Bild soll mal <strong>skizzenhaft</strong> zeigen, was ich mir vorstelle:<br />
<a href="http://rapidshare.com/files/126091188/Bsp.png.html" rel="nofollow">http://rapidshare.com/files/126091188/Bsp.png.html</a></p>
<p>Für dieses Problem fand ich das Entwurfsmuster State Pattern passend. Nachfolgend mal eine <strong>grobe Skizze</strong> einer möglichen Implementation:</p>
<pre><code class="language-cpp">class State
{
	public:
		// Ctors, Dtors...
		virtual ~State()=0 {}

		//nachfolgend mit Default-Implementierung (Bspw. InvalidOperation)
		virtual void doSomethingA();
		virtual void doSomethingB();
		virtual void doSomethingAC();
		virtual void switchAB();

	private:
		std::string name;

		// evtl. noch: Tabelle mit Befehl / Ereignis(?) und Nachfolge-Zustand
};

//konkrete Implementierungen der in Zuständen gültigen Operationen
class StateA : public State
{
	public:
		virtual void doSomethingA()
			{
				iface.NowDoItReally(); //liefert etwas, könnte aber viel zu lange dauern

				// Mit Ausführung von iface.NowDoItReally(), wechsel zu u. bleib im Zustand BusyA
				// Rückkehr in diesen Zustand, wenn
				//	- BusyA TimeOut gibt ODER
				//	- iface.NowDoItReally() vorher ein Ergebnis hat
			}

		virtual void doSomethingAC()
			{
				//Wechsel nur zu Zustand C und mache in diesem weiter ODER
				//mach hier schon was und C ist nur Ergebniszustand (noch offen)
			}

		virtual void switchAB();

	private:
		SomeInterface* iface;
};

class StateB : public State
{
	public:
		virtual void doSomethingB();
		virtual void switchAB();

}

class StateC : public State { /*NIX*/}

class BusyA : public State
{
	public:
		//Ctor: Startet Thread, in dem einfach runtergezählt wird

		//Danach muss irgendwie ein &quot;TimeOut-Handler&quot; gerufen werden, der diesen Zustand wieder
		//zurücksetzt (löscht, den alten wiederherstellt)
}
class BusyB : public State {...}

class Device
{
	public:
		void doSomethingA() {try state.doSomethingA(); catch /*usw. eventuell nochmal wiederholen*/}
		void doSomethingB() {try state.doSomethingB(); catch /*Blaa*/ }
		void doSomethingAC() {try state.doSomethingAC(); catch /*Blaa*/}

	private:
		State* state;

} dev1, dev2, dev3 ... ; //&lt;--- viele voneinander _unabhängige_ devices
</code></pre>
<p>Problem 1:</p>
<p>Für die Transitionen (Zustandsübergänge) bräuchte ich noch eine Funktion / Klasse, die entsprechend <em>Device::state</em> kennt und ändern kann. Dazu könnte sie den alten Zustand löschen (oder sichern) und einen neuen erzeugen. Die Zustände wiederum müssten diese Funktion / Klasse kennen und entsprechend aufrufen, da sie ja selber wissen, welches ihr Nachfolgezustand ist.</p>
<p>Wo ich irgendwie hängenbleibe, ist z.b. der Wechsel in der Methode <em>StateA::doSomethingA()</em>. Wenn ich vor dem Aufruf von <em>iface.nowDoItReally()</em> den Zustand im Device-Objekt mit Hilfe dieser Wechsler-Funktion von StateA in BusyA wechsle, also quasi den Pointer lösche, den BusyA-Zustand (Thread?) erzeuge und zum eigentlichen Aufruf zurückkehren möchte, ist das StateA-Objekt ja nicht mehr gültig.</p>
<p>Problem 2:</p>
<p>Ich bin auch mit dem Thread-Starten in den Busy-Zuständen nicht so sicher, ob das auch so sinnvoll ist. Schliesslich muss bei Beendigung des Countdowns das TimeOut propagiert werden, entweder über den Ruf einer Callback-Funktion, die dann den Zustand wieder zurücksetzt in Device u.ä.</p>
<p>Aber vielleicht ist das ganze Design ja auch einfach nicht geeignet dafür, hmm... was meint ihr dazu?</p>
<p>Gruß, Aquae</p>
]]></description><link>https://www.c-plusplus.net/forum/topic/216915/design-state-pattern-zustände-mit-thread</link><generator>RSS for Node</generator><lastBuildDate>Sat, 10 Oct 2026 23:02:59 GMT</lastBuildDate><atom:link href="https://www.c-plusplus.net/forum/topic/216915.rss" rel="self" type="application/rss+xml"/><pubDate>Mon, 30 Jun 2008 15:50:40 GMT</pubDate><ttl>60</ttl><item><title><![CDATA[Reply to [Design] State Pattern, Zustände mit Thread on Mon, 30 Jun 2008 15:50:40 GMT]]></title><description><![CDATA[<p>Hi,</p>
<p>mal wieder ein Problem, was mir im Kopf rumschwirrt und wo ich andere Meinungen brauche:</p>
<p>Ausgangslage: Ich habe eine Klasse (Abbildung eines Device, davon gibt es viele Objekte), welche mehrere Funktionen unterstützt, die je nach Zustand der Klasse allerdings nicht immer alle verfügbar sind. Nach aussen hin soll diese Abhängigkeit bei der Benutzung nicht ersichtlich sein (und nicht möglich). Befehle, die im falschen Zustand ausgeführt werden sollen, kann man zwischenpuffern.</p>
<p>Das folgende Bild soll mal <strong>skizzenhaft</strong> zeigen, was ich mir vorstelle:<br />
<a href="http://rapidshare.com/files/126091188/Bsp.png.html" rel="nofollow">http://rapidshare.com/files/126091188/Bsp.png.html</a></p>
<p>Für dieses Problem fand ich das Entwurfsmuster State Pattern passend. Nachfolgend mal eine <strong>grobe Skizze</strong> einer möglichen Implementation:</p>
<pre><code class="language-cpp">class State
{
	public:
		// Ctors, Dtors...
		virtual ~State()=0 {}

		//nachfolgend mit Default-Implementierung (Bspw. InvalidOperation)
		virtual void doSomethingA();
		virtual void doSomethingB();
		virtual void doSomethingAC();
		virtual void switchAB();

	private:
		std::string name;

		// evtl. noch: Tabelle mit Befehl / Ereignis(?) und Nachfolge-Zustand
};

//konkrete Implementierungen der in Zuständen gültigen Operationen
class StateA : public State
{
	public:
		virtual void doSomethingA()
			{
				iface.NowDoItReally(); //liefert etwas, könnte aber viel zu lange dauern

				// Mit Ausführung von iface.NowDoItReally(), wechsel zu u. bleib im Zustand BusyA
				// Rückkehr in diesen Zustand, wenn
				//	- BusyA TimeOut gibt ODER
				//	- iface.NowDoItReally() vorher ein Ergebnis hat
			}

		virtual void doSomethingAC()
			{
				//Wechsel nur zu Zustand C und mache in diesem weiter ODER
				//mach hier schon was und C ist nur Ergebniszustand (noch offen)
			}

		virtual void switchAB();

	private:
		SomeInterface* iface;
};

class StateB : public State
{
	public:
		virtual void doSomethingB();
		virtual void switchAB();

}

class StateC : public State { /*NIX*/}

class BusyA : public State
{
	public:
		//Ctor: Startet Thread, in dem einfach runtergezählt wird

		//Danach muss irgendwie ein &quot;TimeOut-Handler&quot; gerufen werden, der diesen Zustand wieder
		//zurücksetzt (löscht, den alten wiederherstellt)
}
class BusyB : public State {...}

class Device
{
	public:
		void doSomethingA() {try state.doSomethingA(); catch /*usw. eventuell nochmal wiederholen*/}
		void doSomethingB() {try state.doSomethingB(); catch /*Blaa*/ }
		void doSomethingAC() {try state.doSomethingAC(); catch /*Blaa*/}

	private:
		State* state;

} dev1, dev2, dev3 ... ; //&lt;--- viele voneinander _unabhängige_ devices
</code></pre>
<p>Problem 1:</p>
<p>Für die Transitionen (Zustandsübergänge) bräuchte ich noch eine Funktion / Klasse, die entsprechend <em>Device::state</em> kennt und ändern kann. Dazu könnte sie den alten Zustand löschen (oder sichern) und einen neuen erzeugen. Die Zustände wiederum müssten diese Funktion / Klasse kennen und entsprechend aufrufen, da sie ja selber wissen, welches ihr Nachfolgezustand ist.</p>
<p>Wo ich irgendwie hängenbleibe, ist z.b. der Wechsel in der Methode <em>StateA::doSomethingA()</em>. Wenn ich vor dem Aufruf von <em>iface.nowDoItReally()</em> den Zustand im Device-Objekt mit Hilfe dieser Wechsler-Funktion von StateA in BusyA wechsle, also quasi den Pointer lösche, den BusyA-Zustand (Thread?) erzeuge und zum eigentlichen Aufruf zurückkehren möchte, ist das StateA-Objekt ja nicht mehr gültig.</p>
<p>Problem 2:</p>
<p>Ich bin auch mit dem Thread-Starten in den Busy-Zuständen nicht so sicher, ob das auch so sinnvoll ist. Schliesslich muss bei Beendigung des Countdowns das TimeOut propagiert werden, entweder über den Ruf einer Callback-Funktion, die dann den Zustand wieder zurücksetzt in Device u.ä.</p>
<p>Aber vielleicht ist das ganze Design ja auch einfach nicht geeignet dafür, hmm... was meint ihr dazu?</p>
<p>Gruß, Aquae</p>
]]></description><link>https://www.c-plusplus.net/forum/post/1538672</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/1538672</guid><dc:creator><![CDATA[[[global:former_user]]]]></dc:creator><pubDate>Mon, 30 Jun 2008 15:50:40 GMT</pubDate></item><item><title><![CDATA[Reply to [Design] State Pattern, Zustände mit Thread on Mon, 30 Jun 2008 17:25:01 GMT]]></title><description><![CDATA[<p>Hey Aquae</p>
<p>Das Pattern erscheint mir sehr geeignet, doch. Zum ersten Problem vielleicht folgendes:</p>
<p>Soll StateA::doSomethingA() blockieren, bis entweder ein Timeout oder ein Resultat vorliegt? Du siehst das nämlich schon richtig, dass den Status nicht wechseln kannst, ehe die Funktion returniert. Normalerweise sähe das z.B. so aus:</p>
<pre><code class="language-cpp">// ...
class Device
{
    public:
        void doSomethingA() {
            try {
                getState().doSomethingA();
            } catch(...) {}
            switchState();
        }
        void doSomethingB() {
            try {
                getState().doSomethingB();
            } catch(...) {}
            switchState();
        }
        void doSomethingAC() {
            try {
                getState().doSomethingAC();
            } catch(...) {}
            switchState();
        }
        void switchState() {
            while(state != state-&gt;getNextState()) {
                State *newState = state-&gt;getNextState();
                delete state;
                state = newState;
            }
        }
        State *getState() {
            switchState();
            return state;
        }

    private:
        State* state;
// ...
}
</code></pre>
<p>Ich sähe das also z.B. so, dass StateA ein BusyA Objekt auf dem Heap anlegt und dessen Pointer via State::getNextState() zur Verfügung stellt. BusyA kann ja dann beliebig viele Threads starten, einen zum Arbeiten, einen um die Zeit zu checken. Läuft die Zeit ab oder liegt ein Resultat vor, erstellt BusyA ein weiteres State Objet (was dann halt passt) und zeigt darauf. Standardmässig zeigt getNextState() auf this (also auf sich selbst) und wird erst geändert, nachdem der Status komplett abgearbeitet wurde und gewechselt werden soll.</p>
<p>Ruft jemand getState() auf, sähe er BusyA, solange das läuft. Erst wenn dieses via getNextState() nicht mehr auf sich selbst, sondern auf den neu erstellten Status zeigt, würde sich das ändern.</p>
<p>Problem 2 ergäbe sich dann aus derselben Lösung. Wenn bei einem Timeout ein IllegalState oder ein TimeOutState gefragt ist, wechsle dahin - einfach via getNextState() bereitstellen und Device holt sich ihn dann, wenn es das nächste mal benutzt wird (z.B. wenn getState() aufgerufen wird).</p>
<p>greeetz<br />
Kessi</p>
]]></description><link>https://www.c-plusplus.net/forum/post/1538720</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/1538720</guid><dc:creator><![CDATA[KessiMC]]></dc:creator><pubDate>Mon, 30 Jun 2008 17:25:01 GMT</pubDate></item><item><title><![CDATA[Reply to [Design] State Pattern, Zustände mit Thread on Tue, 01 Jul 2008 12:19:13 GMT]]></title><description><![CDATA[<p>Hey Kessi,</p>
<p>danke für deine Antwort :-).</p>
<p>Sollte noch erwähnen, dass in StateA / StateB viel mehr verschiedene Operationen möglich sind, die auch in die Busy-Zustände resultieren, aber eben was anderes machen. <em>DoSomethingX</em> sollte bloss Stellvertreter sein.</p>
<p>KessiMC schrieb:</p>
<blockquote>
<p>Soll StateA::doSomethingA() blockieren, bis entweder ein Timeout oder ein Resultat vorliegt?</p>
</blockquote>
<p>Also ich denke ja, es sollte blockieren, allerdings nicht komplett alles. Soll heissen, wenn <em>StateA::doSomethingA()</em> gerufen wird, dann ist keine weitere Operation in diesem Zustand möglich (wie StateA::doSomethingA_1 ...), sondern es muss gewartet werden, bis ein TimeOut vorliegt, oder das Resultat der Operation. Währenddessen ist das Device im Busy-Zustand.<br />
Von aussen soll aber durchaus noch auf die Operationen des Device zugegriffen werden können. Ist es im falschen Zustand oder Busy, wird eine Exception geworfen, oder die Aufrufe werden zwischengepuffert und später im richtigen Zustand ausgeführt (noch offen).</p>
<p>Hab mir deinen Code mal angesehen, durchgespielt und einige Fragen dazu:</p>
<p>Sehe ich das richtig, dass du die eigentliche Ausführung (was ich mit <em>iface.nowDoItReally()</em> in <em>StateA::DoSomethingA()</em> bezeichnet hatte) in den BusyA-Zustand verschiebst?<br />
D.h. <em>StateA::DoSomethingA()</em> sorgt <strong>nur</strong> für die Transition und macht sonst nix?</p>
<p>Und noch zum <em>switchState()</em>:<br />
Angenommen, wir sind in StateA und rufen <em>Device::DoSomethingA()</em>. Wir kommen also in den Try-Block und rufen <em>getState()</em>, welches wiederum <em>switchState()</em> aufruft. In dem While-Schleifenkopf stellen wir fest, dass der nächste Zustand der Busy-Zustand ist, treten in die Schleife ein und erzeugen den neuen Busy-Zustand. Dieser wiederum startet die beiden Threads und stellt seinen getNextState-Pointer auf this, solange nicht mindestens einer fertig ist. &quot;Gleichzeitig&quot; kehrt die Kontrolle wieder zurück in die Schleife, löscht den alten Zustand (StateA) und setzt auf BusyA. Nun läuft die Schleife, wobei sie jedesmal prüft, ob BusyA seinen getNextPointer auf (einen neuen) StateA (also zurück) setzt.</p>
<p>Hab ich das so richtig verstanden? Wann wird denn dann getState()<strong>.doSomethingA()</strong> aufgerufen, erst dann wenn getState() wieder zu StateA (d.h. doSomethingA von StateA) zurückkehrt, also fertig ist?<br />
Was sollte dann StateA::doSomethingA() in seinem Körper machen?</p>
<p>Da hab ich noch Verständnisprobleme, hmm.</p>
<p>Bzgl. der Überwachung der Threads gäbe es ja auch 2 Varianten, oder? Die eine ist deine vorgestellte: Sollte einer beiden fertig sein, stellt BusyA seinen nextState auf StateA, welches von aussen ständig geprüft wird.<br />
Die andere wäre: BusyA benachrichtigt jemanden, oder?</p>
]]></description><link>https://www.c-plusplus.net/forum/post/1539083</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/1539083</guid><dc:creator><![CDATA[[[global:former_user]]]]></dc:creator><pubDate>Tue, 01 Jul 2008 12:19:13 GMT</pubDate></item><item><title><![CDATA[Reply to [Design] State Pattern, Zustände mit Thread on Wed, 02 Jul 2008 10:20:57 GMT]]></title><description><![CDATA[<p>Hi nochmals</p>
<p>Also, die Schleife in switchState() sollte eigentlich nie im Sinne eines Busy Wait laufen, sondern beenden, sobald der aktuell aktive Zustand durch state referenziert wird.</p>
<p>Ein Beispiel: doSomethingA() wird ausgeführt, was eigentlich nur einen Wechsel von StateA zu BusyA verursacht. Bevor jemand wieder (z.B. via getState()) auf Device zugreift, beendet BusyA seine Funktion, erstellt einen neuen StateA und verweist mit getNextState() darauf.</p>
<p>Wenn nun jemand z.B. getState() ausführt, um den aktuellen Status des Gerätes zu erhalten, muss dieser erst via switchState() ermittelt werden. Momentan zeigt Device::state nämlich noch auf das ursprüngliche StateA Objekt. Dessen getNextState() zeigt aber auf ein BusyA-Objekt, also zieht die Schleife, state wird auf das BusyA-Objekt umgeleitet und das StateA-Objekt gelöscht. Das neue BusyA-Objekt zeigt allerdings erneut auf ein anderes Objekt, die Schleife wird erneut durchlaufen, das BusyA-Objekt gelöscht und Device::state zeigt auf das zweite StateA-Objekt.</p>
<p>Dieses 2. StateA-Objekt zeigt nun allerdings mit getNextState() auf sich selbst (das soll so sein, solange ein Status aktiv ist), weshalb die Schleife abbricht und der Status zurückgegeben werden kann. Da auch alle anderen Device-Operationen getState() verwenden, anstatt direkt auf state zuzugreifen, bist du immer sicher, den aktuell aktiven Status zu haben (wobei du dich höchstens noch gegen Multithreading-Anomalien absichern musst). Eine Schleife ist es nur deshalb, weil mehrere State-Wechsel vorkommen könnten, ehe erneut ein switchState() aufgerufen wird.</p>
<p>Im Beispiel würde das also heissen, dass doSomethingA() sofort returnieren würde, nachdem es lediglich einen neues BusyA()-Objekt erstellt hat und via getNextState() darauf verweist. Wird switchState() direkt am Ende von doSomethingA() aufgerufen, würde das BusyA-Objekt auch gleich als Device::state registriert - obwohl das nicht zwingend nötig ist, da das ja eh beim nächsten getState() erledigt wird. Damit wird eigentlich nur verhindert, dass nicht unnötig Objekte auf dem Heap rumschwirren, die eigentlich gar nicht mehr gebraucht werden - was allerdings eh passieren kann und weshalb ich mich persönlich mit einem switchState() in getState() begnügen würde.</p>
<p>Zu deinen anderen Fragen: Ja, die Ausführung der eigentlichen Arbeit würde dann im BusyA Objekt gestartet, welches z.B. gleich im Konstruktor die dazu nötigen Threads erstellt. Finde ich auch irgendwie logisch - das Ding heisst nicht umsonst Busy <img
      src="https://www.c-plusplus.net/forum/plugins/nodebb-plugin-emoji/emoji/emoji-one/1f61b.png?v=ab1pehoraso"
      class="not-responsive emoji emoji-emoji-one emoji--face_with_tongue"
      title=":P"
      alt="😛"
    /> . Das zu koordinieren stellt dich allerdings wieder vor die üblichen PnProg-Probleme: Was, wenn ein TimeOut genau zur gleichen Zeit wie ein Resultat vorliegt? Im Timeout-Fall müsstest du den Rechenthread darüber informieren, dass er beenden soll (z.B. über ein simples Flag) und umgekehrt. Und du müsstest irgendwo die beiden Threads dann auch wieder POSIX-mässig joinen (da böte sich erneut die getNextState()-Methode oder der Destruktor an), damit keine verwaisten Threads herumstreunen <img
      src="https://www.c-plusplus.net/forum/plugins/nodebb-plugin-emoji/emoji/emoji-one/1f642.png?v=ab1pehoraso"
      class="not-responsive emoji emoji-emoji-one emoji--slightly_smiling_face"
      title=":)"
      alt="🙂"
    /> .</p>
<p>Die ganze Arbeit in den BusyA zu verlegen hätte dann, wie bereits erwähnt, zur Folge, dass doSomethingA sofort returnieren würde und einen Statuswechsel provozierte. Von da aus kannst du dann ja beliebig Exceptions werfen, sollte nochmal jemand auf doSomethingX zugreifen - das hängt von deiner Implementation von BusyA ab. Und BusyA könnte selbstverständlich auch zusätzlich noch Callback-Funktionen aufrufen, ganz wie es dir beliebt. Falls das deinem Programmfluss hilft und lästiges Polling auf Device::getState() vermeidet - warum nicht.</p>
<p>greeetz</p>
]]></description><link>https://www.c-plusplus.net/forum/post/1539563</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/1539563</guid><dc:creator><![CDATA[KessiMC]]></dc:creator><pubDate>Wed, 02 Jul 2008 10:20:57 GMT</pubDate></item></channel></rss>