Sealed Java State Machines

Vor ein paar Jahren habe ich darüber geschrieben, wie man Zustandsmaschinen implementiert, die nur gültige Übergänge zur Kompilierungszeit in Java zulassen.

Dies verwendete Schnittstellen anstelle von Enums , was einen großen Nachteil hatte — Sie konnten nicht garantieren, dass Sie alle beteiligten Zustände kennen. Jemand könnte einen anderen Status an anderer Stelle in Ihrer Codebasis hinzufügen, indem er die Schnittstelle implementiert.

Java 15 bringt eine Vorschau-Funktion von versiegelten Klassen. Versiegelte Klassen ermöglichen es uns, dieses Problem zu lösen. Jetzt können unsere schnittstellenbasierten Zustandsautomaten nicht nur ungültige Übergänge verhindern, sondern auch wie Enums aufzählbar sein.

Wenn Sie jdk 15 mit aktivierten Vorschaufunktionen verwenden, können Sie den Code ausprobieren. So sieht es aus, eine Zustandsmaschine mit Schnittstellen zu definieren.

 versiegelte Schnittstelle TrafficLight erweitert den Status<TrafficLight> erlaubt Grün, SolidAmber, FlashingAmber, Rot {}static final class Green implementiert TrafficLight, transitionTo<SolidAmber> {}static final class SolidAmber implementiert TrafficLight, transitionTo<Red> {}static final class Red implementiert TrafficLight, transitionTo<FlashingAmber> {}static letzte Klasse FlashingAmber implementiert Ampel, Übergang zu < Grün> {}

Das neue Teil ist “versiegelt” und “versiegelt”. Jetzt wird es zu einem Kompilierungsfehler, eine neue Implementierung von TrafficLight

Sowie das vorhandene Verhalten zu definieren, bei dem es sich um einen Kompilierungsfehler handelt, einen Übergang durchzuführen, den Ampeln nicht zulassen.

n.b. Sie können auch die zur Kompilierungszeit geprüfte Version überspringen und trotzdem die Typdefinitionen verwenden, um die Übergänge zur Laufzeit zu überprüfen.

Mehrere Übergänge sind auch aus einem Zustand möglich

 statische letzte Klasse Pending implementiert OrderStatus, BiTransitionTo<CheckingOut, Abgebrochen> {}

Dank versiegelter Klassen können wir jetzt auch Enum-Style-Enumerationen und -Lookups auf unseren schnittstellenbasierten Zustandsmaschinen durchführen.

 versiegelte Schnittstelle OrderStatus erweitert den Status<OrderStatus> Genehmigungen Ausstehend, Auschecken, Gekauft, Versendet, Storniert, Fehlgeschlagen, Erstattet {} @Test public void enumerable() { assertArrayEquals( array(Ausstehend.klasse, Check-Out.klasse, Gekauft.klasse, Klasse.klasse, Abgesagt.klasse, Gescheitert.klasse, Zurückerstattet.klasse), Zustand.werte(OrderStatus.klasse) ); assertEquals(0, new Pending().ordinal ()); assertEquals(3, neu)().ordinal()); assertEquals().klasse, Staat.valueOf(OrderStatus.klasse, "Gekauft")); assertEquals(Abgebrochen.klasse, Staat.valueOf(OrderStatus.klasse, "Abgesagt"));}

Diese sind möglich, da JEP 360 eine Reflection API bereitstellt, mit der man die zulässigen Unterklassen einer Schnittstelle aufzählen kann. (randnotiz der JEP sagt getPermittedSubclasses() aber die Implementierung scheint permittedSubclasses() zu verwenden)
Wir können dies hinzufügen, um die oben genannten Komfortmethoden zu unserer Statusschnittstelle hinzuzufügen, um die values() , ordinal() und valueOf() Lookups.

 static <T erweitert State<T>> List<Class> valuesList(Class<T> stateMachineType) { assertSealed(stateMachineType); Stream zurückgeben.von(stateMachineType.permittedSubclasses()) .karte (Zustand :: classFromDesc) .sammeln(ToList());} static <T extends State<T>> Class<T> valueOf(Class<T> stateMachineType, String name) { assertSealed(stateMachineType); return valuesList(stateMachineType) .stream() .filter(c -> Objects.equals(c.getSimpleName(), name)) .findFirst() .orElseThrow(IllegalArgumentException::new);}static <T extends State<T>, U extends T> int ordinal(Class<T> stateMachineType, Class<U> instanceType) { return valuesList(stateMachineType).indexOf(instanceType);}

Im ursprünglichen Beitrag finden Sie weitere Details zur Funktionsweise der Übergangsprüfung und weitere Beispiele, wo dies nützlich sein könnte. Code ist auf Github.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht.