Sealed Java State Machines

数年前、Javaでコンパイル時に有効な遷移のみを許可する状態マシンを実装する方法について投稿しました。

これはenumの代わりにインターフェイスを使用しましたが、大きな欠点がありました—関係するすべての状態を知っていることを保証することはできま 誰かがインターフェイスを実装することによって、コードベースの他の場所に別の状態を追加することができます。

Java15は、封印されたクラスのプレビュー機能を提供します。 封印されたクラスは、この欠点を解決することを可能にします。 今、私たちのインターフェイスベースの状態マシンは、無効な遷移を防ぐだけでなく、列挙型のように列挙可能にすることができます。プレビュー機能を有効にしてjdk15を使用している場合は、コードを試すことができます。 これは、インターフェイスを持つ状態マシンを定義する方法です。

sealed interface TrafficLight extends State<TrafficLight>permissions Green,SolidAmber,FlashingAmber,Red{}static final class Green implements TrafficLight,TransitionTo<SolidAmber>{}static final class SolidAmber implements TrafficLight,TransitionTo<Red>{}static final class Red implements TrafficLight,TransitionTo<FlashingAmber>{}static final class Red implements TrafficLight,TransitionTo<FlashingAmber>{}static final class Red implements TrafficLight,TransitionTo<FlashingAmber>{}static final class Red implements TrafficLight,TransitionTo<FlashingAmber>{}staticファイナルクラス<グリーン> {}

新しい部分は”密封された”および”許可”である。 これで、TrafficLight

の新しい実装を定義するコンパイル失敗と、traffic light

、およびtraffic light

n.b.コンパイル時にチェックされたバージョンをスキップしても、型定義を使用して遷移を実行時にチェックすることもできます。

状態からも複数の遷移が可能です

静的な最終クラス保留中の実装OrderStatus,BiTransitionTo<CheckingOut,Cancelled> {}

sealedクラスのおかげで、インタフェースベースの状態マシンでenumスタイルの列挙と検索を行うこともできます。

sealed interface OrderStatus extends State<OrderStatus>permissions Pending,CheckingOut,Purchased,Shipped,Cancelled,Failed,Refunded{}@Test public void enumerable(){assertArrayEquals(array(Pending.クラス、チェックアウト。クラス、購入しました。クラス、出荷されました。クラス、キャンセルされました。クラス、失敗しました。クラス、返金されました。クラス)、状態。値(OrderStatus.クラス));assertEquals(0,new Pending().序数());assertEquals(3,new Shipped().順序数());assertEquals(購入しました。クラス、状態。valueOf(OrderStatus.クラス、"購入"));assertEquals(キャンセルされました。クラス、状態。valueOf(OrderStatus.クラス、"キャンセル"));}

JEP360は、インターフェイスの許可されたサブクラスを列挙できるリフレクションAPIを提供しているため、これらが可能です。 (jepはgetPermittedSubclasses()と言っていますが、実装はpermittedSubclasses()を使用しているようです)
use thisを追加して、上記の便利なメソッドをstateインターフェイスに追加して、values()、ordinal()、およびvalueOf()の検索を許可

static<T extends State<T>>List<Class>valuesList(Class<T>stateMachineType){assertSealed(stateMachineType);ストリームを返します。の(stateMachineType.許可されたサブクラス())。マップ(State::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);}

遷移チェックがどのように機能するかについての詳細と、これが元の投稿で役立つ可能性のある場所の例があります。 コードはgithubにあります。

コメントを残す

メールアドレスが公開されることはありません。