Skip to content

Commit 30464da

Browse files
author
Nilay Vishwakarma
committed
Send state in sideeffects
1 parent d121d14 commit 30464da

13 files changed

Lines changed: 153 additions & 133 deletions

example/GenericFsm.cs

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
namespace SharpFsm.Example
2+
{
3+
public class GenericFsm
4+
{
5+
public enum OrderState
6+
{
7+
Created,
8+
Paid,
9+
Packed,
10+
Shipped,
11+
Delivered,
12+
Cancelled,
13+
Returned
14+
}
15+
16+
public class OrderContext
17+
{
18+
public bool PaymentReceived { get; set; }
19+
public bool PackingComplete { get; set; }
20+
public bool Shipped { get; set; }
21+
public bool Delivered { get; set; }
22+
public bool CancelRequested { get; set; }
23+
public bool ReturnRequested { get; set; }
24+
}
25+
26+
public GenericFsm()
27+
{
28+
var registry = new TransitionRegistry<OrderState, OrderContext>();
29+
registry.RegisterCondition("PaymentReceived", ctx => ctx.PaymentReceived);
30+
registry.RegisterCondition("PackingComplete", ctx => ctx.PackingComplete);
31+
registry.RegisterCondition("Shipped", ctx => ctx.Shipped);
32+
registry.RegisterCondition("Delivered", ctx => ctx.Delivered);
33+
registry.RegisterCondition("CancelRequested", ctx => ctx.CancelRequested);
34+
registry.RegisterCondition("ReturnRequested", ctx => ctx.ReturnRequested);
35+
36+
registry.RegisterSideEffect("NotifyShipment", (ctx, _, _) => Console.WriteLine("Customer notified: Order shipped"));
37+
registry.RegisterSideEffect("NotifyDelivery", (ctx, _, _) => Console.WriteLine("Customer notified: Order delivered"));
38+
registry.RegisterSideEffect("NotifyCancel", (ctx, _, _) => Console.WriteLine("Customer notified: Order cancelled"));
39+
registry.RegisterSideEffect("NotifyReturn", (ctx, _, _) => Console.WriteLine("Customer notified: Order returned"));
40+
41+
var builder = FiniteStateMachineBuilder<OrderState, OrderContext>.Create("Order")
42+
.WithInitialState(OrderState.Created)
43+
.WithRegistry(registry)
44+
.AddTransition(OrderState.Created, OrderState.Paid)
45+
.When("PaymentReceived")
46+
.Done()
47+
.AddTransition(OrderState.Paid, OrderState.Packed)
48+
.When("PackingComplete")
49+
.Done()
50+
.AddTransition(OrderState.Packed, OrderState.Shipped)
51+
.When("Shipped")
52+
.WithSideEffect("NotifyShipment")
53+
.Done()
54+
.AddTransition(OrderState.Shipped, OrderState.Delivered)
55+
.When("Delivered")
56+
.WithSideEffect("NotifyDelivery")
57+
.Done()
58+
.AddTransition(OrderState.Created, OrderState.Cancelled)
59+
.When("CancelRequested")
60+
.WithSideEffect("NotifyCancel")
61+
.Done()
62+
.AddTransition(OrderState.Paid, OrderState.Cancelled)
63+
.When("CancelRequested")
64+
.WithSideEffect("NotifyCancel")
65+
.Done()
66+
.AddTransition(OrderState.Packed, OrderState.Cancelled)
67+
.When("CancelRequested")
68+
.WithSideEffect("NotifyCancel")
69+
.Done()
70+
.AddTransition(OrderState.Shipped, OrderState.Cancelled)
71+
.When("CancelRequested")
72+
.WithSideEffect("NotifyCancel")
73+
.Done()
74+
.AddTransition(OrderState.Delivered, OrderState.Returned)
75+
.When("ReturnRequested")
76+
.WithSideEffect("NotifyReturn")
77+
.Done();
78+
79+
var fsm = new FiniteStateMachine<OrderState, OrderContext>(builder.Build());
80+
81+
var context = new OrderContext { PaymentReceived = true };
82+
fsm.TryTransitionTo(OrderState.Paid, context); // Moves to Paid
83+
84+
context.PackingComplete = true;
85+
fsm.TryTransitionTo(OrderState.Packed, context); // Moves to Packed
86+
87+
context.Shipped = true;
88+
fsm.TryTransitionTo(OrderState.Shipped, context); // Moves to Shipped, notifies shipment
89+
90+
context.Delivered = true;
91+
fsm.TryTransitionTo(OrderState.Delivered, context); // Moves to Delivered, notifies delivery
92+
93+
context.ReturnRequested = true;
94+
fsm.TryTransitionTo(OrderState.Returned, context); // Moves to Returned, notifies return
95+
}
96+
}
97+
}

example/Program.cs

Lines changed: 2 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using SharpFsm;
2+
using SharpFsm.Example;
23
using SharpFsm.Serialization;
34
using System.Text.Json;
45
using YamlDotNet.Serialization;
@@ -8,93 +9,7 @@ class Program
89
{
910
static void Main()
1011
{
11-
var registry = new TransitionRegistry<OrderContext>();
12-
registry.RegisterCondition("PaymentReceived", ctx => ctx.PaymentReceived);
13-
registry.RegisterCondition("PackingComplete", ctx => ctx.PackingComplete);
14-
registry.RegisterCondition("Shipped", ctx => ctx.Shipped);
15-
registry.RegisterCondition("Delivered", ctx => ctx.Delivered);
16-
registry.RegisterCondition("CancelRequested", ctx => ctx.CancelRequested);
17-
registry.RegisterCondition("ReturnRequested", ctx => ctx.ReturnRequested);
18-
19-
registry.RegisterSideEffect("NotifyShipment", ctx => Console.WriteLine("Customer notified: Order shipped"));
20-
registry.RegisterSideEffect("NotifyDelivery", ctx => Console.WriteLine("Customer notified: Order delivered"));
21-
registry.RegisterSideEffect("NotifyCancel", ctx => Console.WriteLine("Customer notified: Order cancelled"));
22-
registry.RegisterSideEffect("NotifyReturn", ctx => Console.WriteLine("Customer notified: Order returned"));
23-
24-
var builder = FiniteStateMachineBuilder<OrderState, OrderContext>.Create("Order")
25-
.WithInitialState(OrderState.Created)
26-
.WithRegistry(registry)
27-
.AddTransition(OrderState.Created, OrderState.Paid)
28-
.When("PaymentReceived")
29-
.Done()
30-
.AddTransition(OrderState.Paid, OrderState.Packed)
31-
.When("PackingComplete")
32-
.Done()
33-
.AddTransition(OrderState.Packed, OrderState.Shipped)
34-
.When("Shipped")
35-
.WithSideEffect("NotifyShipment")
36-
.Done()
37-
.AddTransition(OrderState.Shipped, OrderState.Delivered)
38-
.When("Delivered")
39-
.WithSideEffect("NotifyDelivery")
40-
.Done()
41-
.AddTransition(OrderState.Created, OrderState.Cancelled)
42-
.When("CancelRequested")
43-
.WithSideEffect("NotifyCancel")
44-
.Done()
45-
.AddTransition(OrderState.Paid, OrderState.Cancelled)
46-
.When("CancelRequested")
47-
.WithSideEffect("NotifyCancel")
48-
.Done()
49-
.AddTransition(OrderState.Packed, OrderState.Cancelled)
50-
.When("CancelRequested")
51-
.WithSideEffect("NotifyCancel")
52-
.Done()
53-
.AddTransition(OrderState.Shipped, OrderState.Cancelled)
54-
.When("CancelRequested")
55-
.WithSideEffect("NotifyCancel")
56-
.Done()
57-
.AddTransition(OrderState.Delivered, OrderState.Returned)
58-
.When("ReturnRequested")
59-
.WithSideEffect("NotifyReturn")
60-
.Done();
61-
62-
var fsm = new FiniteStateMachine<OrderState, OrderContext>(builder.Build());
63-
64-
var context = new OrderContext { PaymentReceived = true };
65-
fsm.TryTransitionTo(OrderState.Paid, context); // Moves to Paid
66-
67-
context.PackingComplete = true;
68-
fsm.TryTransitionTo(OrderState.Packed, context); // Moves to Packed
69-
70-
context.Shipped = true;
71-
fsm.TryTransitionTo(OrderState.Shipped, context); // Moves to Shipped, notifies shipment
72-
73-
context.Delivered = true;
74-
fsm.TryTransitionTo(OrderState.Delivered, context); // Moves to Delivered, notifies delivery
75-
76-
context.ReturnRequested = true;
77-
fsm.TryTransitionTo(OrderState.Returned, context); // Moves to Returned, notifies return
12+
GenericFsm genericFsm = new GenericFsm();
7813
}
7914
}
8015

81-
public enum OrderState
82-
{
83-
Created,
84-
Paid,
85-
Packed,
86-
Shipped,
87-
Delivered,
88-
Cancelled,
89-
Returned
90-
}
91-
92-
public class OrderContext
93-
{
94-
public bool PaymentReceived { get; set; }
95-
public bool PackingComplete { get; set; }
96-
public bool Shipped { get; set; }
97-
public bool Delivered { get; set; }
98-
public bool CancelRequested { get; set; }
99-
public bool ReturnRequested { get; set; }
100-
}

src/EnumStateMachineDefinition.cs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@
77
/// <summary>
88
/// Represents a state machine definition based on an enumeration type.
99
/// </summary>
10-
/// <typeparam name="TEnum">State Enum Type</typeparam>
10+
/// <typeparam name="TState">State Enum Type</typeparam>
1111
/// <typeparam name="TContext">Context Type</typeparam>
12-
public class EnumStateMachineDefinition<TEnum, TContext> : IStateMachineDefinition<TContext> where TEnum : struct, Enum
12+
public class EnumStateMachineDefinition<TState, TContext> : IStateMachineDefinition<TState, TContext> where TState : struct, Enum
1313
{
1414
/// <summary>
1515
/// The type of work item this state machine is associated with.
@@ -24,7 +24,7 @@ public class EnumStateMachineDefinition<TEnum, TContext> : IStateMachineDefiniti
2424
/// <summary>
2525
/// A collection of transitions between states, represented as <see cref="ITransition{TContext}"/> instances.
2626
/// </summary>
27-
public IEnumerable<ITransition<TContext>> Transitions { get; }
27+
public IEnumerable<ITransition<TState, TContext>> Transitions { get; }
2828

2929
/// <summary>
3030
/// The initial state of the state machine, represented as an <see cref="IState"/> instance.
@@ -38,12 +38,12 @@ public class EnumStateMachineDefinition<TEnum, TContext> : IStateMachineDefiniti
3838
/// <param name="states"></param>
3939
/// <param name="transitions"></param>
4040
/// <param name="initialState"></param>
41-
public EnumStateMachineDefinition(string entityType, IEnumerable<TEnum> states, IEnumerable<ITransition<TContext>> transitions, TEnum initialState)
41+
public EnumStateMachineDefinition(string entityType, IEnumerable<TState> states, IEnumerable<ITransition<TState, TContext>> transitions, TState initialState)
4242
{
4343
EntityType = entityType;
44-
States = states.Select(s => new EnumState<TEnum>(s)).ToList();
44+
States = states.Select(s => new EnumState<TState>(s)).ToList();
4545
Transitions = transitions;
46-
InitialState = new EnumState<TEnum>(initialState);
46+
InitialState = new EnumState<TState>(initialState);
4747
}
4848
}
4949
}

src/EnumTransition.cs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@
55
/// <summary>
66
/// Represents a transition in a finite state machine defined by an enumeration.
77
/// </summary>
8-
/// <typeparam name="TEnum">State Enum Type</typeparam>
8+
/// <typeparam name="TState">State Enum Type</typeparam>
99
/// <typeparam name="TContext">Context Type</typeparam>
10-
public class EnumTransition<TEnum, TContext> : ITransition<TContext> where TEnum : struct, Enum
10+
public class EnumTransition<TState, TContext> : ITransition<TState, TContext> where TState : struct, Enum
1111
{
1212
/// <summary>
1313
/// The state this transition is from, represented as an <see cref="IState"/> instance.
@@ -27,7 +27,7 @@ public class EnumTransition<TEnum, TContext> : ITransition<TContext> where TEnum
2727
/// <summary>
2828
/// An optional side effect that occurs when the transition is taken, represented as an action that takes the context.
2929
/// </summary>
30-
public Action<TContext> SideEffect { get; }
30+
public Action<TContext, TState, TState> SideEffect { get; }
3131

3232
/// <summary>
3333
/// The name of the condition that triggers this transition, if any.
@@ -48,10 +48,10 @@ public class EnumTransition<TEnum, TContext> : ITransition<TContext> where TEnum
4848
/// <param name="effect"></param>
4949
/// <param name="conditionName"></param>
5050
/// <param name="sideEffectName"></param>
51-
public EnumTransition(TEnum from, TEnum to, Func<TContext, bool> condition = null, Action<TContext> effect = null, string conditionName = null, string sideEffectName = null)
51+
public EnumTransition(TState from, TState to, Func<TContext, bool> condition = null, Action<TContext, TState, TState> effect = null, string conditionName = null, string sideEffectName = null)
5252
{
53-
From = new EnumState<TEnum>(from);
54-
To = new EnumState<TEnum>(to);
53+
From = new EnumState<TState>(from);
54+
To = new EnumState<TState>(to);
5555
Condition = condition;
5656
SideEffect = effect;
5757
ConditionName = conditionName;

src/FiniteStateMachine.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ public override bool TryTransitionTo(TState target, TContext context)
5858
if (transition is null)
5959
return false;
6060

61-
transition.SideEffect?.Invoke(context);
61+
transition.SideEffect?.Invoke(context, Current, target);
6262
_current = new EnumState<TState>(target);
6363
return true;
6464
}

src/FiniteStateMachineBuilder.cs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,9 @@ public class FiniteStateMachineBuilder<TState, TContext> where TState : struct,
1414
{
1515
private readonly string _entityType;
1616
private TState? _initial;
17-
private TransitionRegistry<TContext> _registry;
17+
private TransitionRegistry<TState, TContext> _registry;
1818
private readonly HashSet<TState> _states = new HashSet<TState>();
19-
private readonly List<ITransition<TContext>> _transitions = new List<ITransition<TContext>>();
19+
private readonly List<ITransition<TState, TContext>> _transitions = new List<ITransition<TState, TContext>>();
2020

2121
/// <summary>
2222
/// Initializes a new instance of the <see cref="FiniteStateMachineBuilder{TState, TContext}"/> class with the specified entity type.
@@ -49,7 +49,7 @@ public FiniteStateMachineBuilder<TState, TContext> WithInitialState(TState state
4949
/// <param name="registry"></param>
5050
/// <returns></returns>
5151
/// <exception cref="ArgumentNullException"></exception>
52-
public FiniteStateMachineBuilder<TState, TContext> WithRegistry(TransitionRegistry<TContext> registry)
52+
public FiniteStateMachineBuilder<TState, TContext> WithRegistry(TransitionRegistry<TState, TContext> registry)
5353
{
5454
if (registry == null)
5555
throw new ArgumentNullException(nameof(registry));
@@ -113,7 +113,7 @@ public class TransitionBuilder
113113
private readonly TState _from;
114114
private readonly TState _to;
115115
private Func<TContext, bool> _condition = _ => true;
116-
private Action<TContext> _sideEffect = null;
116+
private Action<TContext, TState, TState> _sideEffect = null;
117117
private string _conditionName;
118118
private string _sideEffectName;
119119

@@ -170,7 +170,7 @@ public TransitionBuilder When(string name)
170170
/// <param name="effect"></param>
171171
/// <param name="name"></param>
172172
/// <returns></returns>
173-
public TransitionBuilder WithSideEffect(Action<TContext> effect, string name = null)
173+
public TransitionBuilder WithSideEffect(Action<TContext, TState, TState> effect, string name = null)
174174
{
175175
_sideEffect = effect;
176176
_sideEffectName = name;
@@ -222,7 +222,7 @@ public FiniteStateMachineBuilder<TState, TContext> Done()
222222
/// <param name="dto"></param>
223223
/// <param name="registry"></param>
224224
/// <returns></returns>
225-
public FiniteStateMachineBuilder<TState, TContext> LoadFrom(SerializableStateMachine dto, TransitionRegistry<TContext> registry)
225+
public FiniteStateMachineBuilder<TState, TContext> LoadFrom(SerializableStateMachine dto, TransitionRegistry<TState, TContext> registry)
226226
{
227227
_initial = (TState)Enum.Parse(typeof(TState), dto.InitialState);
228228
foreach (var s in dto.States)

src/IStateMachineDefinition.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
namespace SharpFsm
22
{
3+
using System;
34
using System.Collections.Generic;
45

56
/// <summary>
67
/// Represents a definition of a state machine, which includes its states, transitions, and initial state.
78
/// </summary>
89
/// <typeparam name="TContext"></typeparam>
9-
public interface IStateMachineDefinition<TContext>
10+
public interface IStateMachineDefinition<TState, TContext> where TState : struct, Enum
1011
{
1112
/// <summary>
1213
/// Gets the type of work item this state machine is associated with.
@@ -21,7 +22,7 @@ public interface IStateMachineDefinition<TContext>
2122
/// <summary>
2223
/// Gets a collection of transitions between states in the state machine.
2324
/// </summary>
24-
IEnumerable<ITransition<TContext>> Transitions { get; }
25+
IEnumerable<ITransition<TState, TContext>> Transitions { get; }
2526

2627
/// <summary>
2728
/// Gets the initial state of the state machine.

src/ITransition.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,9 @@
55
/// <summary>
66
/// Represents a transition between two states in a finite state machine.
77
/// </summary>
8+
/// <typeparam name="TState">State Type</typeparam>
89
/// <typeparam name="TContext">Context Type</typeparam>
9-
public interface ITransition<TContext>
10+
public interface ITransition<TState, TContext> where TState : struct, Enum
1011
{
1112
/// <summary>
1213
/// Gets the source state of the transition.
@@ -26,7 +27,7 @@ public interface ITransition<TContext>
2627
/// <summary>
2728
/// Gets an optional side effect that occurs when the transition is taken.
2829
/// </summary>
29-
Action<TContext> SideEffect { get; }
30+
Action<TContext, TState, TState> SideEffect { get; }
3031

3132
/// <summary>
3233
/// Gets the name of the condition that triggers this transition, if any.

0 commit comments

Comments
 (0)