Java – Building a Warehouse/Inventory management system, with multiple locations for each item

javaobject-orientedobject-oriented-design

I am building a WMS with the following basic requirements:

  1. Each Item can be in multiple Locations.
  2. A Location can contain multiple Items.
  3. For each Location / item combination, I must be able to track:
    QtyOnHand, QtyReserved, and QtyAvailable.
  4. One Location can contain another Location.

The basic required behaviour of a Location, is:

  1. Adding/Removing an Item with a Quantity.
  2. Adding/Removing a nested Location.
  3. Tracking Quantities for each Item within the Location.

I am attempting to write the code while staying faithful to OOP design principles, namely:

  1. Designing class interfaces to reflect behaviour rather than data.
  2. Avoiding Getter/Setters.
  3. Tell, don't Ask, as much as possible.

With that in mind, here is the basic code for a Location:

public class Location{

    private final Map<InventoryItem, Integer> items = new HashMap<>();
    private final Set<Location> locations = new HashSet<>();

    public void add(InventoryItem item, int quantity) {
         if (items.containsKey(item)) {
             InventoryQuantity inventoryQuantity = items.get(item);
             items.put(item, inventoryQuantity.addQuantityOnHand(quantity));
         } else {
             items.put(item, quantity);
         }
    }

    public void add(Location location) {
        locations.add(location);
    }

    public void remove(InventoryItem item, int quantity) {
        if (items.keySet().contains(item)) {
            if ((items.get(item) - quantity) == 0) {
                items.remove(item);
            } else {
                items.put(item, items.get(item) - quantity);
            }
        }
    }

    public void remove(Location location) {
        locators.remove(location);
    }

}

So I now that I have the basic behaviour for Adding/Removing, I am having difficulty figuring out what would be the best way to track quantity. I could create an InventoryQuantity class, as follows:

 public class InventoryQuantity {

    private InventoryItem item;

    private int quantityOnHand;

    private int quantityReserved;

    private int quantityAvailable;

    public InventoryItem getItem() {
        return item;
    }

    public void setItem(InventoryItem item) {
        this.item = item;
    }

    public int getQuantityOnHand() {
        return quantityOnHand;
    }

    public void setQuantityOnHand(int quantityOnHand) {
        this.quantityOnHand = quantityOnHand;
    }

    public int getQuantityReserved() {
        return quantityReserved;
    }

    public void setQuantityReserved(int quantityReserved) {
        this.quantityReserved = quantityReserved;
    }

    public int getQuantityAvailable() {
        return quantityAvailable;
    }

    public void setQuantityAvailable(int quantityAvailable) {
        this.quantityAvailable = quantityAvailable;
    }
}

and store one InventoryQuantity for each InventoryItem in Location, like so:

private final Map<InventoryItem, InventoryQuantity> items = new HashMap<>();

My questions/issues are as follows:

  1. InventoryQuantity does not seem to be a real world object with a defined behavior; rather, it seems to be a data structure with the sole purpose of storing quantity data (as evidenced by the existance of Getter/Setters); is there a better way?
  2. Is asking a location to getQuantityOnHandForItem(item) a violation of Tell, don't Ask? On the other hand, there does not seem to be a better way of retreiving data…

I actually had some more questions, but this is what I remember at the moment. If anyone can help me understand/apply the principles effectively, I would greatly appreciate it!

Best Answer

Would it make sense in your system to have a class like this:

public class QuantifiedInventoryItem extends InventoryItem
{
    private int quantityOnHand;

    private int quantityReserved;

    private int quantityAvailable;

    /* getters and setters for fields above, if necessary */
}

I agree that it seems a little weird to have an object like the InventoryQuantity as you have it above. It has no behaviour, and does not map to a real-world object.

A quantified InventoryItem can map to a real-world object (well, it maps better than InventoryQuantity): an inventory item with known quantities, maybe even at specific locations.

As far as:

Is asking a location to getQuantityOnHandForItem(item) a violation of Tell, don't Ask?

No, I don't think that's a violation. I think "Tell, Don't Ask" refers more to situations where you have complex logic that depends on the internal state of an object, and that state is accessed via many getters. The pricinciple refers to moving that logic/behaviour into the object, since that's where the data that drives it already resides.

In the case of getQuantityOnHandForItem, you're not asking about specific behaviour driven by this function, so the simple existence of the function is probably not a problem. If you had a set of logic statements that constantly called a Location's getters, it might be worth considering refactoring that logic to a new function within the Location.

See: https://martinfowler.com/bliki/TellDontAsk.html

Related Topic