views:

123

answers:

2

Below is my implementation of the state pattern. In order to persist the State object to my database with NHibernate, I am assigning each state class an enum value. This is stored as a private field on the entity, and mapped to a integer field in my database table.

I want to know whether this is a good implementation as I will be using the state pattern throughout my application and want to get it right the first time. Thanks

public class Order
{
    private OrderStatusEnum _statusId;
    public virtual Guid Id { get; set; }

    private OrderState _status;
    public virtual OrderState Status {
        get
        {
            if (_status == null)
                _status = GetState(_statusId);
            return _status;
        }
        set
        {
            _status = value;
            _statusId = _status.Id;
        }
    }

    private OrderState GetState(OrderStatusEnum status)
    {
        switch (_statusId) {
            case OrderStatusEnum.Pending:
                return new Submitted(this);
            case OrderStatusEnum.Completed:
                return new Completed(this);
            default:
                return new NewOrder(this);
        }
    }
}

public abstract class OrderState
{
    private readonly Order _order;

    public OrderState(Order order) {
        _order = order;
    }

    internal Order Order { get { return _order; } }
    public abstract OrderStatusEnum Id { get; }

    public virtual void Submit() {
        throw new InvalidOperationException(
            string.Format("Can't Submit a {0} Order", this.GetType().Name)
        );
    }

    public virtual void Complete() {
        throw new InvalidOperationException(
                string.Format(string.Format("Can't Cancel a {0} Order", this.GetType().Name))
            );
    }

    protected internal void _Submit() {
        Order.Status = new Submitted(Order);
    }

    protected internal void _Complete() {
        Order.Status = new Completed(Order);
    }
}

public class NewOrder : OrderState
{
    public NewOrder(Order order) : base(order) { }

    public override OrderStatusEnum Id {
        get { return OrderStatusEnum.New; }
    }

    public override void Submit() {
        _Submit();
    }
}

public class Submitted : OrderState
{
    public Submitted(Order order) : base(order) { }

    public override OrderStatusEnum Id {
        get { return OrderStatusEnum.Pending; }
    }

    public override void Complete() {
        _Complete();
    }
}

public class Completed : OrderState
{
    public Completed(Order order) : base(order) { }

    public override OrderStatusEnum Id {
        get { return OrderStatusEnum.Completed; }
    }
}

public enum OrderStatusEnum {
    New = 1,
    Pending = 2,
    Completed = 3
}
A: 

I've used the above approach in a number of NHibernate applications and am very happy with it.

Ben
A: 

Not sure whether to answer or add a comment, but your approach worked very well for me in a similar situation.

I also experimented with the approach described here using the Tarantino framework, but I found it easier to extend from your code.

David