Machines d’état Java scellées

Il y a quelques années, j’ai posté sur la façon d’implémenter des machines d’état qui n’autorisent que des transitions valides au moment de la compilation en Java.

Cela utilisait des interfaces au lieu d’énumérations, ce qui avait un gros inconvénient — vous ne pouviez pas garantir que vous connaissiez tous les états impliqués. Quelqu’un pourrait ajouter un autre état ailleurs dans votre base de code en implémentant l’interface.

Java 15 apporte une fonctionnalité d’aperçu des classes scellées. Les classes scellées nous permettent de résoudre cet inconvénient. Maintenant, nos machines d’état basées sur l’interface peuvent non seulement empêcher les transitions invalides, mais aussi être énumérables comme des énumérations.

Si vous utilisez jdk 15 avec les fonctionnalités de prévisualisation activées, vous pouvez essayer le code. Voici à quoi ressemble la définition d’une machine d’état avec des interfaces.

 interface scellée TrafficLight étend l'état < TrafficLight > autorise Green, SolidAmber, FlashingAmber, Red {} classe finale statique Green implémente TrafficLight, TransitionTo < SolidAmber > {} classe finale statique SolidAmber implémente TrafficLight, TransitionTo < Red > {} classe finale statique Red implémente TrafficLight, TransitionTo < FlashingAmber > {} static FlashingAmber de classe finale implémente TrafficLight, TransitionTo < Vert> {}

La nouvelle pièce est “scellée” et “permet”. Maintenant, il devient un échec de compilation de définir une nouvelle implémentation de TrafficLight

Ainsi que le comportement existant où il s’agit d’un échec de compilation pour effectuer une transition que les feux de circulation ne permettent pas.

N.B. vous pouvez également ignorer la version vérifiée au moment de la compilation et utiliser toujours les définitions de type pour vérifier les transitions.

Plusieurs transitions sont également possibles à partir d’un état

 la classe finale statique En attente implémente OrderStatus, BiTransitionTo < CheckingOut, Annulé> {}

Grâce aux classes scellées, nous pouvons également effectuer une énumération et des recherches de style enum sur nos machines d’état basées sur l’interface.

 l'interface scellée OrderStatus étend l'état < OrderStatus > permet En attente, la vérification, l'Achat, l'expédition, l'Annulation, l'échec, le Remboursement {} @ Test public void enumerable() { assertArrayEquals(array(Pending.classe, Check-out.classe, Acheté.classe, Expédiée.cours annulé.la classe a échoué.classe, Remboursée.classe), État.valeurs (OrderStatus.class)); assertEquals(0, new Pending().ordinal()); assertEquals(3, nouveau expédié().ordinal()); assertEquals (Acheté.classe, État.Valeur de (État de la commande.classe, "Acheté")); assertEquals (Annulé.classe, État.Valeur de (État de la commande.classe " Annulée"));}

Cela est possible car JEP 360 fournit une API de réflexion avec laquelle on peut énumérer les sous-classes autorisées d’une interface. (note latérale le JEP dit getPermittedSubclasses() mais l’implémentation semble utiliser permittedSubclasses())
Nous pouvons ajouter ceci pour ajouter les méthodes de commodité ci-dessus à notre interface d’état pour autoriser les recherches values(), ordinal() et valueOf().

 static < T étend l'état < T > > List < Class > valuesList(Class < T > stateMachineType) { assertSealed(stateMachineType); Flux de retour.de (Type de machine d'État.permittedSubclasses()).carte (État::classFromDesc).collect(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);}

Il y a plus de détails sur le fonctionnement de la vérification de transition et plus d’exemples d’endroits où cela pourrait être utile dans le message d’origine. Le code est sur github.

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée.