pattern-matching.cs
public static class PatternMatching
{
public static U Match<U, T1, T2>(Func<T1, U> match1, Func<T2, U> match2, T1? value1, T2? value2)
{
if (value1 is not null)
return match1(value1);
if (value2 is not null)
return match2(value2);
throw new InvalidOperationException("Match should have least 1 valid value");
}
public static void Match<T1, T2>(Action<T1> match1, Action<T2> match2, T1? value1, T2? value2)
{
if (value1 is not null)
match1(value1);
if (value2 is not null)
match2(value2);
}
}
order.cs
namespace StatePattern.Order;
public interface IOrderType;
public record struct Sell() : IOrderType;
public record struct Buy() : IOrderType;
public abstract record IOrderState;
public record Open() : IOrderState;
public record Closed() : IOrderState;
public record Unchecked() : IOrderState;
public readonly record struct IOrderTransition<T> where T : IOrderType
{
public Order<T, Open>? OrderOpen { get; init; } = null;
public Order<T, Closed>? OrderClosed { get; init; } = null;
public IOrderTransition(Order<T, Open> order)
{
OrderOpen = order;
}
public IOrderTransition(Order<T, Closed> order)
{
OrderClosed = order;
}
public U Match<U>(Func<Order<T, Open>, U> open, Func<Order<T, Closed>, U> closed)
=> PatternMatching.Match(open, closed, OrderOpen, OrderClosed);
public void Match(Action<Order<T, Open>> open, Action<Order<T, Closed>> closed)
=> PatternMatching.Match(open, closed, OrderOpen, OrderClosed);
}
public record Order<TType, TState>(
Guid Id,
double Price,
uint PendingShares
)
where TType : IOrderType
where TState : IOrderState;
public record Order
{
internal static Order<T, Unchecked> SubtractPendingShares<T>(Order<T, Open> order, uint quantity)
where T : IOrderType
{
ArgumentOutOfRangeException.ThrowIfGreaterThan(quantity, order.PendingShares);
return new(
Id: order.Id,
Price: order.Price,
PendingShares: order.PendingShares - quantity
);
}
}
public static class UncheckedOrder
{
public static IOrderTransition<T> Check<T>(this Order<T, Unchecked> order)
where T : IOrderType
=> order.PendingShares == 0
? new(new Order<T, Closed>(
Id: order.Id,
Price: order.Price,
PendingShares: 0
))
: new(new Order<T, Open>(
Id: order.Id,
Price: order.Price,
PendingShares: order.PendingShares
));
}
public static class BuyOrder
{
public static Order<Buy, Unchecked> BuyAssets(this Order<Buy, Open> order, uint quantity)
{
Console.WriteLine("Some buy order logic");
// Fazer incremento do Investidor e caso sucesso prosseguir
return Order.SubtractPendingShares(order, quantity);
}
}
public static class SellOrder
{
public static Order<Sell, Unchecked> SellAssets(this Order<Sell, Open> order, uint quantity)
{
Console.WriteLine("Some sell order logic");
// Fazer decremento do Investidor e caso sucesso prosseguir
return Order.SubtractPendingShares(order, quantity);
}
}
program.cs
using StatePattern.Order;
var order = new Order<Buy, Open>(Id: Guid.NewGuid(), Price: 10.5d, PendingShares: 15);
var order2 = new Order<Sell, Open>(Id: Guid.NewGuid(), Price: 10.5d, PendingShares: 10);
order.BuyAssets(10)
.Check()
.Match(
open: order => Console.WriteLine($"Kept Open BUY: {order}"),
closed: order => Console.WriteLine($"Closed BUY: {order}")
);
order2.SellAssets(10)
.Check()
.Match(
open: order => Console.WriteLine($"Kept Open SELL: {order}"),
closed: order => Console.WriteLine($"Closed SELL: {order}")
);