public static void Main()
var shipment = new Shipment(Guid.NewGuid(), "RU", "Istanbul");
shipment.ToCustomsCleared();
shipment.ToOutForDelivery();
shipment.ToNotDelivered();
Console.WriteLine("-----------------------------------------------------------------");
var shipment1 = new Shipment(Guid.NewGuid(), "TR", "Istanbul");
shipment1.ToCustomsCleared();
shipment1.ToOutForDelivery();
shipment1.ToNotDelivered();
public abstract class ShipmentState
public virtual void InTransit(Shipment shipment)
Console.WriteLine(GetInvalidMessage(nameof(InTransit), shipment.State));
public virtual void InCustoms(Shipment shipment)
Console.WriteLine(GetInvalidMessage(nameof(InCustoms), shipment.State));
public virtual void CustomsCleared(Shipment shipment)
Console.WriteLine(GetInvalidMessage(nameof(CustomsCleared), shipment.State));
public virtual void OutForDelivery(Shipment shipment)
Console.WriteLine(GetInvalidMessage(nameof(OutForDelivery), shipment.State));
public virtual void Delivered(Shipment shipment)
Console.WriteLine(GetInvalidMessage(nameof(Delivered), shipment.State));
public virtual void NotDelivered(Shipment shipment)
Console.WriteLine(GetInvalidMessage(nameof(NotDelivered), shipment.State));
protected string GetInvalidMessage(string newState, ShipmentState currentState) =>
$"{newState} cannot be performed in {currentState.GetType().Name}.";
public class OrderPlacedState : ShipmentState
public override void InTransit(Shipment shipment)
if (shipment.FromCountry == "TR")
Console.WriteLine(GetInvalidMessage(nameof(InTransit), shipment.State));
Console.WriteLine("The cargo has been loaded onto the ship.");
shipment.MoveTo(new InTransitState());
public override void OutForDelivery(Shipment shipment)
if (shipment.FromCountry != "TR")
Console.WriteLine(GetInvalidMessage(nameof(OutForDelivery), shipment.State));
Console.WriteLine("The cargo is out for delivery.");
shipment.MoveTo(new OutForDeliveryState());
public class InTransitState : ShipmentState
public override void InCustoms(Shipment shipment)
Console.WriteLine("The cargo has arrived at customs.");
shipment.MoveTo(new InCustomsState());
public class InCustomsState : ShipmentState
public override void CustomsCleared(Shipment shipment)
Console.WriteLine("Customs clearance procedures have been completed.");
shipment.MoveTo(new CustomsClearedState());
public class CustomsClearedState : ShipmentState
public override void OutForDelivery(Shipment shipment)
Console.WriteLine("The cargo is out for delivery.");
shipment.MoveTo(new OutForDeliveryState());
public class OutForDeliveryState : ShipmentState
public override void Delivered(Shipment shipment)
Console.WriteLine("The cargo has been delivered.");
shipment.MoveTo(new DeliveredState());
public override void NotDelivered(Shipment shipment)
if (shipment.DeliveryAttempts < 1)
shipment.DeliveryAttempts++;
Console.WriteLine("The cargo could not be delivered. We will attempt delivery again.");
Console.WriteLine("The cargo could not be delivered. The return process has been initiated.");
shipment.MoveTo(new NotDeliveredState());
public class NotDeliveredState : ShipmentState
public class DeliveredState : ShipmentState
public Guid OrderId { get; private set; }
public string FromCountry { get; private set; }
public string To { get; private set; }
public ShipmentState State { get; private set; }
public int DeliveryAttempts { get; internal set; }
public Shipment(Guid orderId, string fromCountry, string to)
FromCountry = fromCountry;
State = new OrderPlacedState();
public void MoveTo(ShipmentState state)
public void ToInTransit()
public void ToInCustoms()
public void ToCustomsCleared()
State.CustomsCleared(this);
public void ToOutForDelivery()
State.OutForDelivery(this);
public void ToDelivered()
public void ToNotDelivered()
State.NotDelivered(this);