State Pattern als Enum?

Rudolf

Bekanntes Mitglied
Hi,

ich kenne das State Pattern als Muster mit normalen Klassen ohne Enumeration. Kann man das State Pattern mit einer enum Klasse umsetzen?

Sehe auf den ersten Blick keine Möglichkeit folgendes sementisch gleich als enum zu realisieren

Java:
public class Test {
	public static void main(final String[] args) {
		new Test().start();
	}
	State state = new StateA(this);
	private void start() {
		state.execute();
	}
	public void setState(State state) {
		this.state = state;
	}
}

abstract class State {
	protected final Test invoker;
	protected State(Test invoker) {
		this.invoker = invoker;
	}

	public abstract void execute();
}

class StateA extends State {
	protected StateA(Test invoker) {
		super(invoker);
	}
	@Override
	public void execute() {
		System.out.println("calcing...");
		final int result = 5;
		final StateB newState = new StateB(result, invoker);
		invoker.setState(newState);
	}
}

class StateB extends State {
	private final int value;
	protected StateB(int result, Test invoker) {
		super(invoker);
		value = result;
	}
	@Override
	public void execute() {
		System.out.println(value);
	}
}

Sowas wäre eine ähnliche Umsetzung, aber ist syntaktisch fehlerhaft:

Java:
public class Test {
	public static void main(final String[] args) {
		new Test().start();
	}
	State state = State.StateA;
	private void start() {
		state.execute(this);
	}
	public void setState(State state) {
		this.state = state;
	}
}

enum State {
	StateA {
		@Override
		public void execute(Test invoker) {
			System.out.println("calcing...");
			final int result = 5;
			invoker.setState(StateB);
			StateB.setValue(result);
		}
	},
	StateB {
		private final int result;
		public void setValue(int value) {
			result = value;
		}
		@Override
		public void execute(Test invoker) {

		}
	};
	public abstract void execute(Test invoker);
}

Kennt jemand eine bessere Umsetzung?
 
Zuletzt bearbeitet:
Ich würde evtl im sinne der flexibilität, udnd amit nicht alles in einer class ist nen Interfae State machen, dann normale classen nehmen, und ne Enum um die gültigen States alle an einem punkt gelsitet zu haben.

Weil wenn ud das so amchst und zb nen state der 5 unterklassen braucht und 1k zeilen lang ist hast wirds minmal unübersichtlich.
 
Ich würde evtl im sinne der flexibilität, udnd amit nicht alles in einer class ist nen Interfae State machen, dann normale classen nehmen, und ne Enum um die gültigen States alle an einem punkt gelsitet zu haben.

Weil wenn ud das so amchst und zb nen state der 5 unterklassen braucht und 1k zeilen lang ist hast wirds minmal unübersichtlich.

Wie meinst du Übersicht? Kannst n Beispiel mit deiner Enum geben?
 
Ich finde es allgemein gesagt nicht sehr schön, wenn ENUM's irgendwelchen Funktionscode enthalten.
Das sorgt - IMHO - nur für Verwirrung und bietet nichts wesentlich eleganteres, als mit Klassen direkt zu arbeiten.

Ich kenne eine Lösung, da wird jeder Zustand als konkrete Klasse implementiert (Zustandsmaschine)
und für das unterscheiden der Zustände wird ein Enum herangenommen. Beispielsweise um von Zustand A nach Zustand B zu wechseln.
 
Ich finde es allgemein gesagt nicht sehr schön, wenn ENUM's irgendwelchen Funktionscode enthalten.
Das sorgt - IMHO - nur für Verwirrung und bietet nichts wesentlich eleganteres, als mit Klassen direkt zu arbeiten.

Ich kenne eine Lösung, da wird jeder Zustand als konkrete Klasse implementiert (Zustandsmaschine)
und für das unterscheiden der Zustände wird ein Enum herangenommen. Beispielsweise um von Zustand A nach Zustand B zu wechseln.

Tatsächlich sind Enums ein extrem brauchbarer Weg um Singletons zu realisieren:
Singleton in Java – the proper way Electrotek

Allerdings ist das Verhalten dann allgemein an das Enum gebunden, beim State Pattern will man ja in der Regel unterschiedliches Verhalten ohne ewiges if(state == ...) bzw switch(state) mittels polymorphie erreichen -> mehrere Klassen.

Gruß
 
Kann man als Fazit aussagen:

Wenn ein erweitertes Boolean-Feld gebraucht wird, bei dem man wenige Unterscheidungen macht, wie z. B. bei

Java:
enum Day {
Montag,Dienstag,Mittwoch;
}

dann nutzt man das enum aber für ein echtes State Pattern ist das Coding mit eigenen Klassen der bessere Weg?
 
Die Frage sollte doch eher sein was dies:
Java:
enum Day {
Montag,Dienstag,Mittwoch;
}

bringen soll?

Damit kann man eigentlich nur was Anfangen, wenn man sich nicht an ein Statepattern orientieren möchte.
Java:
if(day == Montag) ...
if(day == Mittwoch)...
switch(day)
Montag: ...
Dienstag: ...

Enums kann man gut und Sinnvoll für State-Pattern benutzen.
Man muß nur ein paar Dinge beachten:

Mit Enums ist die ganze Geschichte Abgeschlossen.
Java:
enum Day {
Montag, ..., Sonntag
}
Es kann nicht mehr mit Weihnachten, Ostern, Neujahr und sonstigen besonderen Tagen erweitert werden, da das Enum nur Montag bis Sonntag kennt.

Ein Enum darf/sollte keinen "State" beinhalten! Es sollte Stateless sein.
Java:
StateB {
        private final int result;
        public void setValue(int value) {
            result = value;
        }

Also das Feld result ist Fehl am Platz.
Man stelle sich nur vor, daß man mehrere Threads hat, wobei beide ein verschiedenes value setzen und erst dann eine execute Methode aufgerufen wird.

Das Enum Day kann z.B. einen Konstruktor haben, so daß ein Wochentag eine Nummer "Tag der Woche" bekommt.
Dieser State ist dann allerdings Fix und wird beim Initialisieren gesetzt.

Bei vielen Werten und langen Methoden kann das ganze Recht schnell unübersichtlich werden. Bei einer Lösung mit einzelnen Klassen verteilt sich das auf viele Dateien, die ihrerseits übersichtlich bleiben. Das ganze bleibt erweiterbar. Zu den Tagen Montag bis Sonntag, kann auch ein Weihnachten hinzugefügt werden.

Bei enums hat man weniger Sorgen bzgl. der Initialisierung der States gegenüber einzelnen Klassen, die man dann allerdings cachen kann oder on Demand initialisieren. Bei on Demand erzeugt man dann allerdings auch jährlich jeweils über 50 Montage ... Sonntage.

Wie also leicht zu erkennen ist, gibt es nicht wirklich ein Allheilmittel.


Anbei noch ein Video bei dem ein paar Punkte bzgl. dem Thema behandelt werden.

"The Clean Code Talks -- Inheritance, Polymorphism, & Testing" - YouTube
 
Tatsächlich sind Enums ein extrem brauchbarer Weg um Singletons zu realisieren:
Singleton in Java – the proper way Electrotek

Hm nette Idee, aber enum heißt auf deutsch nun mal Aufzählung(Menge) und Singleton ist eine "einelementige Menge" und damit finde ich die Lösung verwirrend und fällt für mich eher in die Kategorie, freaky C++ Code (why simple, if you can do it freaky). So wie ich aber z.b. Robert C. Martin mit Clean Code verstanden habe muss ein guter Code möglichst verständlich geschrieben sein und damit werde ich diese Lösung nicht einsetzen.
 
[OT]
bei einem enum als singleton geht es vorallem darum, dass keine zweite instanz (über reflections, serialization o.ä.) erzeugt werden kann! für sicherheitskritischen- code ist das evtl von bedeutung!
[/OT]
 
sambalmueslie hat gesagt.:
Hm nette Idee, aber enum heißt auf deutsch nun mal Aufzählung(Menge) und Singleton ist eine "einelementige Menge" und damit finde ich die Lösung verwirrend und fällt für mich eher in die Kategorie, freaky C++ Code (why simple, if you can do it freaky). So wie ich aber z.b. Robert C. Martin mit Clean Code verstanden habe muss ein guter Code möglichst verständlich geschrieben sein und damit werde ich diese Lösung nicht einsetzen.
Ich finde das Singleton-Enum-Pattern jetzt nicht unverständlich. Jedenfalls nicht unverständlicher als dieser ganze static-Kram, den man sonst macht, um Singletons zu realisieren. Das Pattern wird von Joshua Bloch (Mitentwickler des JDK) ausdrücklich in seinem Buch empfohlen. Ich werde es also in den seltenen Fällen, wo ich Singletons brauche, benutzen.

Und zur Ursprungsfrage:
Java:
enum State {
    StateA {
        @Override
        public void execute(Test invoker) {
            System.out.println("calcing...");
            final int result = 5;
            invoker.setState(StateB);
            StateB.setValue(result);
        }
    },
    StateB {
        private final int result;
        public void setValue(int value) {
            result = value;
        }
        @Override
        public void execute(Test invoker) {
 
        }
    };
    public abstract void execute(Test invoker);
Lager die abstrakte excecute-Methode in ein public Interface "State" aus. Lass Deine enum dieses Interface implementieren. Programmiere nur gegen dieses Interface. Dass Deine enum package-private ist, ist schonmal ein guter Schritt in diese Richtung. Wenn Dir die State-Enum irgendwann doch zu unflexibel ist, kannst Du sie leicht austauschen, weil der Client nur das Interface sieht.
 
Code/Logik in Enums zu haben ist nix ungewöhnliches in Java, ganz im Gegenteil, da ist nix "tricky" daran, sondern eher Standard in Java.

Empfehle das Josh Blochs "Effective Java 2nd Edition" als Lektüre.

Allerdings ist das State Patterns kein guter Kandidat, eben wegen des Zustandes.
Das Algorythm Pattern dagegen wird meistens als Enum implementiert, bietet sich ja direkt dafür an.
 

Neue Themen


Zurück
Oben