GRASP Patterns – Information Expert – Create a Shopping Cart

I am a huge fan of Object-Oriented Programming and have come to believe that most of our programming challenges can be solved by focusing on the basics.  Although clever solutions to problems can be very cool, it often means that reading through the code can be challenging, time consuming, and require a lot of documentation.


Applying UML and Patterns by Craig Larman discusses several principles, called GRASP, that can help you with the fundamental challenges of object-oriented programming – assigning responsibilities to objects.  I mentioned these GRASP Patterns in a previous post.


The first GRASP Pattern to mention is Information Expert.



  • Pattern Name: Information Expert
  • Problem:  What is a basic principle by which to assign responsibilities to Objects?
  • Solution: Assign a responsibility to the class that has the information needed to fulfill it.

I will be the first to say that this means absolutely nothing unless we have a concrete example.  In fact, it sounds so vague that it doesn’t seem to have any value at all.


Let’s take the code in my post, Create a Shopping Cart, to help clarify the power of this pattern.


In the shopping cart example, we wanted to have each Item show up only once in the cart.  If the same Item is being added to the cart, we wanted to increase the quantity of that Item as opposed to adding additional items in the _items ArrayList.


This programming challenge suggests that we need to determine if the item being added is equal to an item already in the shopping cart.  The question becomes, which object has the responsibility of deciding whether two Items are equivalent?


According to our business rules, two items in the shopping cart are identical if they have equivalent ProductID‘s.  Which object in our design model knows about ProductID?  The answer is Item.  And, according to the Information Expert GRASP Pattern, we should assign the responsibility of determing equality to Item, because it has the information needed to fulfill the responsibility.  As “luck” would have it, this is exactly what we did :)


In our shopping cart example, we used extensive use of the ArrayList.IndexOf method to determine if two Items are equal.  ArrayList.IndexOf uses System.Object’s Equals method that we overrode to determine equality.  Thus, Item is determining equality with respect to other Item instances and the Information Expert GRASP Pattern is being fulfilled.


 


Item Class
public class Item : IFormattable
{
//

public override bool Equals(object obj)
{
if (obj == null) return false;
if (Object.ReferenceEquals(this,obj)) return true;
if (this.GetType() != obj.GetType()) return false;

Item objItem = (Item)obj;
if (_productID == objItem._productID) return true;

return false;
}

//
}


 


As you can tell from the code above, the Item class determines equality with respect to other object’s in the _items ArrayList via overriding System.Object’s Equals method.  Below is the code that uses ArrayList.IndexOf in the Shopping Cart class:


 


ShoppingCart Class
public class ArrayListCart : ShoppingCart
{
private ArrayList _items;

//

public override void AddItem(Item item)
{
int index = _items.IndexOf(item);

if (index >= 0)
UpdateItem(item);
else
_items.Add(
new Item(item.ProductID, item.ProductName,
item.Quantity, item.UnitPrice));
}

//

}


 


The code in the Shopping Cart class AddItem method is small because it delegates the responsibility of equality to the Item class accordingly.  Although not shown in this example, a number of the Shopping Cart methods will also need to check the equality of two items in order to update and delete an Item in the cart.  By assigning responsibility of this function to the Item class, we also remove code smells from our application.

This entry was posted in GRASP Patterns. Bookmark the permalink. Follow any comments here with the RSS feed for this post.

2 Responses to GRASP Patterns – Information Expert – Create a Shopping Cart

  1. dhayden says:

    You’re absolutely right, Thomas. There are some code smells still in the code.

    If I stick with my ideal intention of keeping the constructors on Item internal and not exposing the ArrayList outside of the ShoppingCart class, many of the checks in the overrride of System.Object’s Equals method are not needed. I am trying to stay somewhat consistent with my previous post:

    http://codebetter.com/blogs/david.hayden/archive/2005/02/22/51364.aspx

    that talks about overriding System.Object’s Equals in a very generic case. In this example, however, all those checks are overkill and many of them can be removed.

    And, you are also 100% correct in having Item copy itself. I mentioned in the original Create a Shopping Cart example that creating new Item(…) is not ideal and should be done via a copy constructor, etc.

    In fact, the code I show above is actually flawed as it exists in the shopping cart. I mentioned this in the comments of the Shopping Cart example. I was leaving it to the reader to fix :) as I wasn’t so much trying to create the ultimate Shopping Cart but to discuss fundamental OOP.

    You have me thinking I should probably come out with a Create a Shopping Cart Part II and fix some of the smells and bugs, however, as I don’t want people getting distracted from the details.

    Your comments are awesome and exactly the kind of comments I love to read. Thanks!

  2. Thomas Eyde says:

    I think your code still has some smell in it.

    What kind of needs do you have that made the Item.Equals method look like it does? Do you ever in your code compare something which is not an Item to an Item? Isn’t your _items arraylist protected from such misuse with typesafe, public accessors?

    If you need to check for null, then the following should do:

    public override bool Equals(object obj)
    {
    Item objItem = obj as Item;
    if (objItem == null) return false;
    return (_productID == objItem._productID);
    }

    In the ShoppingCart, why don’t you just add the Item? Why create a new one which seems to be a copy of the one passed? If you need to insulate the added Item instanse from the passed instanse, why don’t you apply your own principle and let the Item create a copy?

    So you could do this:

    public override void AddItem(Item item)
    {
    int index = _items.IndexOf(item);

    if (index >= 0)
    UpdateItem(item);
    else
    _items.Add(item);
    }

    Or, if insulation is needed:

    public override void AddItem(Item item)
    {
    int index = _items.IndexOf(item);

    if (index >= 0)
    UpdateItem(item);
    else
    _items.Add(item.Copy());
    }

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>