2

I have a simple Store class that contains an Inventory. The Inventory contains a list of Items. In order to modify one of the Items in the Inventory, I'd have to write:

Store store( /*parameters*/ );
store.accessInventory(/*password*/).accessItem(/*item name*/).setPrice(9.50);

As I understand it, this breaks the Law of Demeter because Store has to reach through Inventory and into Item in order to call setPrice().

I'd like to reconcile this violation of the law with the violation of the law in the classic example with the paper boy and the customer. In the paper boy example, the paper boy "knows" too much about the customer by assuming he will make his payment with a wallet. If the customer's method of payment changes, the paper boy will have to change as well.

What assumptions in my code are being made that could result in a problem like the one encountered in the paper boy example?

I understand that the Law is really more of a guideline, and that abiding by it in this case may not be the best idea, but I'd like to at least understand the Law before I move on. Thanks.

Attila
  • 28,265
  • 3
  • 46
  • 55
JamesGold
  • 795
  • 2
  • 8
  • 24

3 Answers3

4

Your code is assuming that the Inventory object is the only object that will ever need to be notified when a price changes.

Imagine that in addition to containing an Inventory of items, your Store also had some advertising posters hung up in its windows.

If you followed the law of Demeter, your Store object might have a nice method like this:

void Store :: SetItemPrice(string item_name, float item_price)
{
   inventory.SetItemPrice(item_name, item_price);

   for (int i=0; i<num_advertising_posters; i++)
   {
      // Update any posters with the new price!
      if (advertising_posters[i].advertised_item == item_name)
      {
         advertising_posters[i].SetAdvertisedPrice(item_price);
      }
   }
}

... but if you instead allow the calling code to access the inventory object directly, then there is no easy/foolproof way to make sure that the advertisements always get updated whenever a price gets updated, so chances are that at some point your store's advertising posters will show an old/wrong price for a product. The Law of Demeter makes it easier to avoid that sort of bug.

Jeremy Friesner
  • 70,199
  • 15
  • 131
  • 234
  • But this doesn't seem like the central purpose of the Law. According to this [article](http://www.cmcrossroads.com/bradapp/docs/demeter-intro.html), it seems more that the central purpose of the Law is to mask the specific path taken between the calling method and the destination method, so that the calling method will be unaffected if the path ever changes. The ability to add additional functionality to SetItemPrice() seems like a nice side-effect of the Law, but not the central purpose. – JamesGold May 28 '12 at 06:26
  • @JamesGold I think we are both coming to the same point from slightly different directions. In my example, the code outside of the Store object can remain unchanged despite the introduction of the advertising feature. That is the benefit of masking the specific path. – Jeremy Friesner May 28 '12 at 08:11
  • I don't see how masking the specific path relates to not having to change code outside the Store object when the advertising feature is introduced. – JamesGold May 28 '12 at 11:19
  • @JamesGold: Justifying the LoD in anticipation of future code changes is a case of premature optimization. Jeremy's example of advertisements is a much better justification for the law. – David Hammen May 28 '12 at 13:29
  • `item_name` *should* be passed by value. – David Rodríguez - dribeas May 28 '12 at 16:15
  • @DavidHammen Insulation from future code changes is the justification that's been given for the LoD in every source I've encountered outside of this page. Could you explain why you think it's a case of premature optimization? As I understand it the spirit of the LoD is to reduce coupling between objects, and the introduction of the advertising feature doesn't have much to do with coupling. Or does it? – JamesGold May 28 '12 at 22:41
1

Your example follows the paperboy example very closely. Your code is dependent upon the current Item interface which has the method setPrice(float). If you change that interface you need to update

store.accessInventory(password).accessItem(name).setPrice(price)

everywhere that it occurs.

A better solution would be to create function for Store and Inventory of the form

void Store::setItemPrice(string password, string name, float price)
{
  accessInventory(password).setItemPrice(name, price);
}

void Inventory::setItemPrice(string name, float price)
{
accessItem(name).setPrice(price);
}

This way you can use those functions in the rest of your code and just change them accordingly if your Item or Inventory interfaces change.

Paari Kandappan
  • 199
  • 2
  • 8
1

What assumptions in my code are being made that could result in a problem like the one encountered in the paper boy example?

Angry customers. Angry personnel. Angry managers. Angry accountants.

Customers don't mind being surprised when the price listed is higher than the price they are charged, but they sure do get POed when they are charged a higher price than the price they expected. The worker bees don't like it when angry customers come up to them because the stupid newbie store manager didn't follow protocol and changed the price directly in the inventory. Low and mid level managers don't like this either because they get grief from all sides. Bean counters also like to be informed when the value of their beans suddenly change.

It's not just the advertising posters that Jeremy mentioned in his answer. Some person needs to be told to change the little pricing tags next to the stack of items that specify the price. A crew of people need to be dispatched with portable printers to change the price stamped on each item of that type. These people need to be scheduled, and you had better not do that without talking to the department manager. And so on and so on. Reaching through the inventory to change the price is a bad, bad idea.

BTW, all of the above is coming from someone who thinks the Law Of Demeter is better called the Occasionally Useful Suggestion of Demeter.

David Hammen
  • 32,454
  • 9
  • 60
  • 108