Forseglede Java State Machines

For noen år tilbake postet jeg om hvordan man implementerer statsmaskiner som bare tillater gyldige overganger ved kompileringstid i Java.

dette brukte grensesnitt i stedet for enums, som hadde en stor ulempe-du kunne ikke garantere at du kjenner alle involverte stater. Noen kan legge til en annen stat andre steder i kodebasen ved å implementere grensesnittet.

Java 15 gir en forhåndsvisningsfunksjon av forseglede klasser. Forseglede klasser gjør det mulig for oss å løse denne ulempen. Nå kan våre grensesnittbaserte statsmaskiner ikke bare forhindre ugyldige overganger, men også nummereres som enums.

hvis du bruker jdk 15 med forhåndsvisningsfunksjoner aktivert, kan du prøve koden. Slik ser det ut til å definere en tilstandsmaskin med grensesnitt.

trafficlight extends State<TrafficLight> tillater Grønt, SolidAmber, FlashingAmber, Rødt {}statisk Siste klasse Grønne redskaper TrafficLight, TransitionTo<SolidAmber> {}statisk siste klasse Røde redskaper TrafficLight, TransitionTo<FlashingAmber> {}statisk siste klasse Røde redskaper TrafficLight, TransitionTo<FlashingAmber> {}statisk siste klasse Flashingamber Implementerer Trafficlight, Transitionto<grønn> {}

Den nye delen er “forseglet” og “tillatelser”. Nå blir det en kompileringsfeil for å definere en ny implementering Av TrafficLight

Samt eksisterende oppførsel der det er en kompileringstidssvikt for å utføre en overgang som trafikklys ikke tillater.

n. b. du kan også hoppe over kompileringstid sjekket versjon og fortsatt bruke typedefinisjonene til runtime sjekke overgangene.

Flere overganger er også mulige fra en tilstand

statisk siste klasse Ventende implementerer OrderStatus, BiTransitionTo < CheckingOut, Kansellert> {}

Takket være forseglede klasser kan vi også nå gjøre enum stil opplisting og oppslag på våre grensesnittbaserte statsmaskiner.

forseglet grensesnitt OrderStatus utvider Tilstand<OrderStatus > tillatelser Venter, CheckingOut, Kjøpt, Sendt, Avbrutt, Mislyktes, Refundert {} @ Test offentlig ugyldig enumerable () { assertArrayEquals (array (Venter.klasse, Sjekk Ut.klasse, Kjøpt.klasse, Sendt.Klasse, Avlyst.klasse, Mislyktes.klasse, Refundert.klasse), Stat.verdier (OrderStatus.klasse)); assertEquals (0, ny Ventende ().ordinal ()); assertEquals (3, ny Sendt ().ordinal ()); assertEquals (Kjopt.klasse, Stat.verdiav (OrderStatus.klasse, "Kjøpt")); assertEquals (Kansellert.klasse, Stat.verdiav (OrderStatus.klasse, "Avlyst"));}

Dette er mulig fordi JEP 360 gir en refleksjon API som man kan nummerere de tillatte underklasser av et grensesnitt. (side note JEP sier getPermittedSubclasses () men implementeringen ser ut til å bruke permittedSubclasses ())
vi kan legge til bruk dette til å legge til de ovennevnte bekvemmelighetsmetodene til Vårt Statsgrensesnitt for å tillate verdiene (), ordinal () og valueOf () oppslag.

statisk < t utvider Tilstand < T > > Liste < Klasse > valuesList(Klasse< T > stateMachineType) {assertSealed(stateMachineType); returstrøm.av (stateMachineType.permittedSubclasses()) .kart (Stat:: classFromDesc) .samle(tilliste());} 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);}

det er flere detaljer om hvordan overgangen sjekker fungerer og flere eksempler på hvor dette kan være nyttig i det opprinnelige innlegget. Koden er på github.

Legg igjen en kommentar

Din e-postadresse vil ikke bli publisert.