0

Greetings! I am trying to decide on which is the best approach to implement a following scenario:

Vehicle and Part are both "entites" which can be an Item in an Inventory. Vehicle has the usual VIN, Year, Make, Model, Type, etc, etc... while Part has PartNumber, QuantityOnHand, IsPartOfSet, Vendor... When "stocked" in an Inventory as an Item, both have RetailPrice, PurchasePrice, PurchaseDate but at the same time they have a lot of other properties that are not in common (e.g Vehicle has CurrentMileage, NewOrUsed, etc... etc.. while Part has TreshholdCount, LastCountDate, etc, etc..) So as you can see both Part and Vehicle can be in Inventory, but they have very few properties that are share (more that they don't)...

With scenario above, I am trying to decide between several approaches:

Option 1 - Separate classes for everything (no inheritance)

  • Vehicle - common vehicle properties (VIN, year, make, model, etc, etc)
  • VehicleInventoryItem - has a reference to Vehicle and contains other inventory specific properties (pricing, warranties, status)
  • VehicleInventoryManager - singleton with business logic add, remove, notify, etc, etc
  • Part
  • PartInventoryItem
  • PartInventoryManager

I think this approach is most flexible while it suffers for some property repetition. Managers also can have very different methods...

Option 2 - Base class simple inheritance

  • Vehicle - same as option 1
  • Part - same as option 1
  • InventoryItem - base class
  • VehicleInventoryItem - extends InventoryItem, references Vehicle
  • PartInventoryItem - extends InventoryItem, references Part
  • InventoryManager - all methods accept InventoryItem as parameters

With this approach, I am worried about InventoryManager being polluted with different operations needed for parts vs vehicles

Option 3 - Generics

  • Item - where T will be part or vehicle (I can even go more specific then, e.g. - VehicleItem : Item)
  • Inventory - holds reference to Item
  • InventoryManager - singleton that operates on T

In this approach, there is less classes but again I am worried about InventoryManager

Recap

  1. Vehicle and Part share very few properties when "stocked" in Inventory.
  2. Inventory management for Vehicle and Part will also be mostly different (parts are "reordered" when qty on hand is low, when vehicle is sold similar one may be purchased but it is unique)
  3. In my scenario there will be no other objects/models that will be "stockable" (being in Inventory of any kind)

Questions

What does community think? Is there another approach (perhaps interfaces?) If 2 domain objects share at least 1 property (e.g. PurchasePrice), should that warrant having some sort of inheritance (e.g. option 2 or 3)

Sisyphus
  • 4,181
  • 1
  • 22
  • 15
zam6ak
  • 7,229
  • 11
  • 46
  • 84

4 Answers4

2

I'd create an abstract base class representing any item that is potentially held in inventory. Something like InventoryItem. It would have the basic properties you mention that everything has in common: RetailPrice, PurchasePrice, PurchaseDate, etc. And it would provide a default implementation (where appropriate) for certain properties and methods.

Then, I would inherit from this class and specialize it for each of the items you plan on stocking: Vehicle and Part. Override the methods that you need to, implement all of the methods marked as abstract, and add any additional functionality and/or business logic that is required by the specific subclasses.

There's absolutely no reason to use an interface here. As you mention, all of the things you sell share many common attributes. They have some extra ones and some of the functions might work differently, but there's a lot that is fundamentally the same. There's no reason to duplicate that functionality across multiple places. Using an abstract base class gives you the chance to provide a default implementation.

You don't need an InventoryManager class at all: you're right to be suspect of it having to be aware of the different types of subclasses. Just let the derived classes do all the work. That's what they're for.

For more on why abstract base classes are generally superior to interfaces, see the following answers:

And I can't imagine why generics are relevant here. They're a specific tool used to solve a specific problem, not a general design pattern. If you need generics, use them. But a well-conceived design that takes advantage of inheritance and polymorphism also makes it a lot less unlikely that you'll need them in the first place.

Community
  • 1
  • 1
Cody Gray - on strike
  • 239,200
  • 50
  • 490
  • 574
  • Thanks for the insight - very informative...I am wondering why the reasoning of not having a "manager" class? Isn't it a good approach to have class that performs all business operations separately from the model (ie business delegate)? In another words, why should Vehicle have logic that would "add" itself to Inventory, especially when there may be other business rules and validations in play? – zam6ak Jun 02 '11 at 15:35
  • @zam6ak: Yeah, that might have been a bit of an overstatement. The truly common business logic could indeed be implemented in a sort of "manager" class (although I, like lots of others, still remain a healthy bit skeptical at both the name and the design pattern). The point is that you want to avoid requiring the Manager class to "know about" the derived classes it's handling. All of the *specific* logic should be located in those *specific* classes, and it should simply delegate to them to perform it (by calling methods on an instance of the base class, of course). – Cody Gray - on strike Jun 02 '11 at 15:37
  • I agree with you regarding single manager knowing specifics of both sublasses (VehicleInventoryItem and PartInventoryItem)..This is why I proposed subclassing a manager as well (see option 2) and then started thinking "maybe this is too much")... – zam6ak Jun 02 '11 at 15:43
  • @zam6ak: From my perspective, inheritance and polymorphism mean that you don't have to. Delegate all of that to the individual classes representing specific inventory items. All the manager has to do is call methods on those objects. Mark them `internal` if you don't want to expose them to external code. – Cody Gray - on strike Jun 02 '11 at 15:44
1

I would create a base class: Let's call it InventoryItem containing all the shared properties (RetailPrice, PurchasePrice, PurchaseDate etc.)

Part and Vehicle would then inherit from InventoryItem and extend it with whatever properties they need.

I would then create a base class called InventoryManager which VehicleManager and PartManager inherits from. InventoryManager would contain all the shared logic for managing the inventory and the sub classes would extend it with whatever functionality they need.

I get the impression that you are pretty early in the design process. I wouldn't bother trying to nail the perfect design for this, this early. Just build something basic and change and extend it as you go along, because when you start to build and use the API you will understand the problem better.

"When in doubt, leave it out."

Fredrik
  • 2,016
  • 3
  • 15
  • 26
  • Not to be accusatory or anything, but this sounds suspiciously like my answer, posted 30 minutes previously. The only thing you've suggested different is to derive subclasses of `InventoryManager`, which I don't think is a particularly good idea. Why maintain a `Part` class and a `PartManager` class separately? Shouldn't the `Part` class just be able to manage itself? If you follow the comment exchange with the OP on my answer, you'll see that I strongly recommend taking advantage of polymorphism to allow the base Manager class to delegate to the specific item subclasses. – Cody Gray - on strike Jun 02 '11 at 15:55
  • Sorry if you feel like I stole your answer. I did read it before I answered this question. But my approach is a bit different that's why I wanted to post my thoughts about it. – Fredrik Jun 02 '11 at 16:11
  • Basically I don't agree that Part and PartManager functionality should reside in the same class because that would break the Seperation Of Concerns principle. If Part knows how to manage himself in an inventory then Part knows too much. I think it's much cleaner to seperate them. – Fredrik Jun 02 '11 at 16:17
  • This is a fair point. Update your answer to talk about why separation of concerns is important, and how your proposed solution solves that problem. There's no "correct answer" to questions tagged "design", of course. – Cody Gray - on strike Jun 02 '11 at 16:39
  • @Fredrik This is similar to approach I gave in my comments to @Cody's answer. However, I don't think it is good approach to extend Part and Vehicle from InventoryItem because they are just entities that "can be" in inventory. Furthermore, extending would mean that every time I want to use Vehicle or Part "outside" of Inventory context I would still have to "carry the excess" properties that are related to inventorying. This is why I have VehicleInventoryItem which extends InventoryItem and holds reference to Vehicle (and same for Part). I do like the approach on subclassing managers though. – zam6ak Jun 03 '11 at 13:13
  • Yeah, it sounds like a good approach. I was in doubt wether Part and Vehicle could be used outside of an inventory context, so that's why I never introduced any more separations. – Fredrik Jun 03 '11 at 13:26
0

From a brief look at your description I would like to suggest putting the Inventory related methods on the items in an Interface. If you need a relationship between vehicles and parts, you can always have a property on your parts to reference the vehicle they come from, or the other way around (or possibly both, if you need to do lookups both ways). This seems to separate the concern of dealing with inventory related tasks into a specific class. Hope that helps.

Martin
  • 920
  • 7
  • 24
0

Maybe I'm missing something, but I don't understand your concern about "pollution" in the InventoryManager. If that's a concern, it would call for a separate class. Every other class you've described shares common functionality (at the very least, their identifiers), so inheritance would seem to be the order of the day.

I think, however, that you're going to want to use a mix of technologies. You'll use inheritance, with interfaces (for comparison, for example), and generics (for lists). One does not necessarily exclude the other.

Mike Hofer
  • 16,477
  • 11
  • 74
  • 110
  • The "pollution" I refer to is when Inventory manager has 4 methods that would work for both Vehicle and Part but then I need 2 specific methods for Parts and 3 more that are specific to Vehicles...I guess I could "deepen" the inheritance (group everything common into base class and extend appropriately) but then I end up with (essentially) case 1... – zam6ak Jun 02 '11 at 15:06