A while ago I noticed that one of the ASP.NET Daily Articles was an article on how to create a shopping cart. Being as I love example code, I went over to the author's website to take a peek at the sample. It wasn't quite what I had hoped for as the shopping cart was just an ArrayList, called _items, filled with objects of type Item that consisted of ProductID, ProductName, Quantity, and UnitPrice. The example ignored many of the rules of object-oriented development like encapsulation, etc.
I thought I would create a better, yet admittedly imperfect example that may reinforce some of the techniques I have presented in previous posts. To start, let's begin with the bare bones Item class that was presented in the ASP.NET daily article example:
Let's pump up this class a bit by overriding Equals and ToString as well as implementing IFormattable that we will use to help display our shopping cart to the console. Let's also have the class implement its ItemTotal (_quanity * _price) to help the shopping cart calculate SubTotal. I consider two Item objects equal if their ProductID's are equal. I will use this criteria to determine if I should update the quantity on an existing item vs adding a new item to the shopping cart.
Now I need a shopping cart to hold these items. Let's create an abstract class, called ShoppingCart, that will define how we want to work with these items in the shopping cart. As you can see by the class, we are not exposing any implementation details to the developer as to how we are storing and manipulating items.
If later, we think we may need an indexer, we might have to add it. Right now, the only access to the items in our shopping cart is IEnumerable, which is read-only access to the data that we can bind to a DataGrid, etc. as well as access via foreach.
Notice I also have a CreateItem() method as well. Ideally I would like to put all these classes in their own assembly. Using this method coupled with the fact that the Item class constructors are marked internal, we can control the construction of new items to be placed in the shopping cart. This is from the Creator GRASP Pattern that suggests a good creator of a class (Item) is the class that contains it (ShoppingCart).
We now need a concrete class that derives from ShoppingCart. Shown below is my ArrayListCart class that essentially holds item in an ArrayList. The class uses extensive use of the ArrayList.IndexOf method to decide whether or not an item currently exists in the _items arraylist. IndexOf uses the Equals method we overrode in the Item class to determine equality. Also, to keep things easy, I pass through the IEnumerator of _items to be used as the IEnumerator for ArrayListCart. This is the same technique I discussed in my IEnumerable post.
Notice how I also create a new Item to be placed into _items as opposed to the one passed as an argument. We don't want the developer to have a reference to any of the items in the shopping cart. In this case it is easy to create a new item, but ideally we might want to clone or create a copy constructor for Item.
Here is a simple Factory Method technique that we discussed in a post about the Data Access Application Block to future proof the possibility of creating additional shopping carts or creating one on-the-fly:
Now we need a class to test ArrayListCart, which ideally should have been created using TDD, but alas I didn't do it. Therefore, the ArrayListCart class could have all sorts of problems and I just don't know it :) Here is some code that when run with the above will output a pretty decent shopping cart to the Console. Don't use this as a shopping cart in your e-commerce websites :)
Unlike the example in the ASP.NET Daily Article, you have absolutely no clue as to how items are being stored, etc. as shown in the test class. This is ideal, because it gives you more freedom to make changes to the shopping cart without breaking your client code.
Posted
Tue, Mar 22 2005 6:38 AM
by
David Hayden