Sealed Java State Machines

et par år tilbage skrev jeg om, hvordan man implementerer statsmaskiner, der kun tillader gyldige overgange på kompileringstidspunktet i Java.

dette brugte grænseflader i stedet for enums, som havde en stor ulempe—du kunne ikke garantere, at du kender alle de involverede stater. Nogen kunne tilføje en anden stat andetsteds i din kodebase ved at implementere grænsefladen.

Java 15 bringer en forhåndsvisning funktion af forseglede klasser. Forseglede klasser gør det muligt for os at løse denne ulempe. Nu kan vores interface-baserede tilstandsmaskiner ikke kun forhindre ugyldige overgange, men også være tællelige som enums.

hvis du bruger jdk 15 med forhåndsvisningsfunktioner aktiveret, kan du prøve koden. Sådan ser det ud til at definere en statsmaskine med grænseflader.

forseglet grænseflade TrafficLight udvider tilstand<TrafficLight> tillader grøn, SolidAmber, FlashingAmber, Rød {}statisk endelig klasse grøn implementerer TrafficLight, Overgangtil<SolidAmber> {}statisk endelig klasse SolidAmber implementerer TrafficLight, Overgangtil<Rød> {}statisk endelig klasse Rød implementerer TrafficLight, Overgangtil<FlashingAmber> {}statisk sidste klasse Flashingamber implementerer trafficlight, Transitionto<grøn> {}

den nye del er “forseglet”og ” tilladelser”. Nu bliver det en kompileringsfejl at definere en ny implementering af TrafficLight

samt den eksisterende adfærd, hvor det er en kompileringstidsfejl at udføre en overgang, som trafiklys ikke tillader.

n.b. Du kan også springe over kompileringstidskontrolleret version og stadig bruge typedefinitionerne til at køre kontrol af overgange.

flere overgange er også mulige fra en tilstand

statisk afsluttende klasse afventer implementerer OrderStatus, BiTransitionTo<CheckingOut, annulleret> {}

takket være forseglede klasser kan vi også nu gøre enum stil optælling og opslag på vores interface baserede statslige maskiner.

forseglet grænseflade OrderStatus udvider tilstand< OrderStatus > tilladelser afventer, CheckingOut, købt, afsendt, annulleret, mislykkedes, refunderet {} @Test public void enumerable() { assertarrayekvaler( array(afventer.klasse, check-out.klasse, købt.klasse, afsendt.klasse, aflyst.klasse, mislykkedes.klasse, refunderet.klasse), stat.værdier (OrderStatus.klasse)); påstå ligeværdige(0, ny verserende ().ordinal ()); påståsvarende(3, nye afsendt ().ordinal ()); påstålige(købt.klasse, stat.værdiaf (OrderStatus.klasse," købt")); hævdesvarer(annulleret.klasse, stat.værdiaf (OrderStatus.klasse, "annulleret"));}

disse er mulige, fordi JEP 360 giver en refleksion API, som man kan opregne de tilladte underklasser af en grænseflade. (sidebemærkning JEP siger getPermittedSubclasses () men implementeringen ser ud til at bruge permittedSubclasses ())
vi kan tilføje Brug dette til at tilføje ovenstående bekvemmelighedsmetoder til vores Statsgrænseflade for at tillade værdierne (), ordinal() og valueOf () opslag.

statisk <t udvider tilstand< T>>liste< klasse>valuesList(klasse< t > stateMachineType) { assertSealed(stateMachineType); returstrøm.af (stateMachineType.tilladte underklasser ()).kort (tilstand:: classFromDesc).indsamle (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);}

der er flere detaljer om, hvordan overgangskontrollen fungerer, og flere eksempler på, hvor dette kan være nyttigt i det originale indlæg. Koden er på github.

Skriv et svar

Din e-mailadresse vil ikke blive publiceret.