1

Pretty simple setup: I've got a Cart and the cart has Items. I've got many varying types of "products" that can't really be boiled down into one thing or even a common ancestor, so I've instead decided to simply create a representation of the product as a CartItem, which contains all the information from the product needed to make the sale.

That's where the question comes in. I could handle this in three ways that I see. For these examples we'll just say I have two types of "products": Widgets and Gizmos. Again, these two things are so vastly different that it doesn't make sense for them to inherit from any base class.

1) Both a Widget and a Gizmo have the ability to create a Cart Item to represent themselves, but this requires both to have knowledge of CartItem and any change to CartItem will require me to touch both Widget and Gizmo (not ideal, obviously)

2) A CartItem can create itself from either a Widget or a Gizmo. This would essentially be a factory pattern and would require CartItem to have knowledge of both Widget and Gizmo. This is better than #1 in that I don't have to make changes to every kind of "product" if I change CartItem, but I do have to remember that if I change a particular "product" then I must change the factory method of CartItem for that. (still not ideal, because Murphy says I will forget).

3) Use something like the controller as the "glue". WidgetController and GizmoController will have AddToCart actions that translate a Widget or Gizmo, respectively, into a CartItem. This really sort of presents the same problems as #1, in that if CartItem changes, I need to update all the "product" controllers, and in fact mixes in the problems of #2, in that I must remember to always maintain this mapping if something on either side changes. However, it feels more like a job for the controller: it's oriented for dealing with multiple types, whereas it just feels wrong to mix in types at the model level.

4) Three that I can see; #4 would be something I have yet to consider that one of you kind individuals may happen to suggest.

I'm open to any comments here. If I'm missing something completely obvious, feel free to call me a moron and point how just how big a moron I am. I like feeling stupid; that's when I'm learning ;).

1
  • Option #2 is a "factory method" pattern. There is another pattern called "abstract factory", which is one step beyond it, and may solve your problem. Basically, Option #3 is simply shoveling the abstract factory's responsibility onto some existing classes called WidgetController and GizmoController; however, it is possible to decouple the abstract factory from them.
    – rwong
    Apr 6, 2013 at 12:39

4 Answers 4

3

The obvious simple solution is to have both Gizmo and Widget implement ICartItem, which would be an interface containing exactly what it takes for something to be represented in a cart, and nothing more (e.g., unit price, quantity, unit, description). Gizmo and Widget could have radically different ways of coming up with these, as long as they implement the interface correctly.

The obvious downside with the obvious simple solution is that now both Gizmo and Widget have to know a few things about shopping carts, which kind of violates the Single Responsibility principle. If you feel this is happening, consider splitting things up: Gizmo and Widget are completely unrelated, without any shared functionality whatsoever; GizmoCartItem and WidgetCartItem both implement the ICartItem interface and know how handle a Gizmo or a Widget as a cart item; the shopping cart still deals strictly with ICartItems; and you probably want some kind of factory that creates ICartItems for Gizmos and Widgets so that you never have to touch the glue classes directly. With this design, the Gizmo doesn't know anything about shopping carts, nor about Widgets; the shopping cart doesn't know anything about Gizmos; and the cart item glue know just enough about both Gizmos and shopping carts to interface them.

1
  • I'm giving you the credit on this one because I think your answer is the closest to the actual implementation I'll end up following. An interface is a great idea, of course, but I like the idea of having a factory that sort of abstracts away the responsibility. All the products need not worry about how they are added to the cart, and the cart need not worry about how products are added to it. Instead, a specialized class(es) take care of this translation. Apr 8, 2013 at 2:03
4

It really smells to me like you want an ICartItem interface. If it's contract needs changing, every single implementer will break the build until they all have been updated appropriately, so it'll keep you honest.

If you're in a dynamically typed language however then I would say the same thing except as an interface exists to be a contract that keeps objects honest, in dynamically typed languages the only technique you have available to keep your objects honest is run-time tests, so I'd write some tests that ensure all the objects you expect to implement the ICartItem interface actually implement all of it's parts with expected signatures. If you're in a static-typed language though then you get these guarantees for free at compile-time.

I think all 3 you're mentioning is thinking too much in the framework of common objects, when common contracts are lighter weight while maintaining greater compile-time guarantees. Common objects are for when you have shared functionality you don't want to repeat, but in your situation you're claiming zero logical shared functionality. You just want to treat different types like the same type, that's the definition of polymorphism and the entire singular purpose of interfaces.

1
  • Thanks for the suggestion of using an interface. I'm relatively new to the world of strongly-typed languages. (I've spent most of my career working with duck-typed languages like Python and Ruby.) I know about interfaces, but they always escape my planning for some reason. Apr 8, 2013 at 2:06
2

It depends a little on how complex it is to create a CartItem, but I would either go with option (1), or option (1) that delegates the actual creation to another class.

(2) violates the open-closed principle - if you are to add a new third product type Gadget you would have to change CartItem so it can create itself from a Gadget as well.

(3) is less reusable than having it in the model. If you need a new controller that serves a different view you would be tempted to call methods from the other WidgetController just to create a CartItem.

Also, I don't think your objections to #1 is justified. I think it's natural that a product knows how to create a CartItem of itself. But if you think it seems smelly, perhaps it is more natural to have a addToCart(cart, quantity) method in Widget and Gizmo instead, hiding where the actual CartItem is created?

1

I would prefer option (2) but put the factory in a separate class. This way you can avoid polluting your products with information about how to make cartitems. If the product changes the factory doesn't really have to change like you have mentioned. Only if the construction of the cartitem changes, which is exactly what the factory is for.

1
  • Good point. You're right. Just because a model changes, doesn't necessarily mean the factory method must change, as long as the constructor itself hasn't changed. Apr 8, 2013 at 1:56

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.