After hours of discussions and documentation, we had a good understanding of the functional requirements.  The ask was for a very complex, intelligent and “AJAXy” web application which would give financial advisors the ability to rebalance their clients’ portfolios based on a specified investment objective.  Everyone was excited (especially me) until the final requirement was issued: “We would like the ability to undo actions.”

The request was reasonable.  If I were an advisor I would wish for this feature as well, but this was a web application with literally dozens of ways to extend, manipulate and delete data. I had concerns but I agree to evaluate and potentially prototype.  Before the meeting closed I was able to set the expectation that we would most likely need to limit the number of undos — after all we weren’t dealing with a web application with unlimited resources.

As it turned out, the implementation worked itself out nicely.  Rather than “logging” each action, I opted to store the state of the data AFTER each action.  For example, instead of being concerned about how a security was added to the client’s portfolio, I started caring only about the resulting new data item.  As form changes were made, I pushed a copy of the new state onto a “LimitedStack.”  When the undo action was triggered, I would then pop the previous state off the stack and update the form accordingly.  Finally, as the stack approach the maximum number of undos, the item in the first position would be removed in order to make room for the new item being push into the last position. 

At the moment, we are allowing for up to 6 undos.  Each undo is being managed within the LimitedStack in Session State.  I am concerned that we are effectively using ~6 times more memory to accommodate this requirement, but I’m hopeful that performance testing will ease my worries. 

I’ve include the LimitedStack logic below.  You will see it is using the LinkedList<T> as its base.

I also found the copying of the state (which was a collections of nested objec references) to be an interesting problem which needed solving.  I hope to comment about deep copying soon.

[Serializable()]
public class LimitedStack<T> : LinkedList<T>
{
    private int _maxItems;

    public int MaxItems
    {
        get { return _maxItems; }
        set
        {
            while (this.Count > value)
            {
                this.RemoveFirst();
            }
            _maxItems = value;
        }
    }

    public LimitedStack(int num)
    {
        _maxItems = num;
    }

    public T Peek()
    {
        return this.Last.Value;
    }

    public T Pop()
    {
        LinkedListNode<T> node = this.Last;
        this.RemoveLast();
        return node.Value;
    }

    public void Push(T value)
    {
        LinkedListNode<T> node = new LinkedListNode<T>(value);
        this.AddLast(node);

        if (this.Count > _maxItems)
        {
            this.RemoveFirst();
        }
    }
}