Start learning free

LESSON 561 OF 573 · OBJECT-ORIENTED PROGRAMMING - CHALLENGES

Inventory Item in Go: Exported and Unexported Fields

Practice O(1) Read the full explanation

A shop keeps track of how many pieces of each item it has: the item's stock. The stock can never go below 0, and it changes only when pieces are sold or new ones arrive. So the stock is a private field, changed only through two methods that check the rules. The item's name has no rules to protect, so any code can read it.

As in the Access Modifiers lesson, Go decides by the first letter of a name: a field that starts with an uppercase letter is exported, visible to code outside the package, and a lowercase one is unexported, hidden from it. Write:

  • The InventoryItem struct, with an exported field Name (a string) and an unexported field stock (an int).
  • NewInventoryItem(name, stock): returns an item with that name and that many pieces in stock.
  • sell(n) sells n pieces. If n is at least 1 and at most the stock, it lowers the stock by n and returns true. Otherwise it changes nothing and returns false: selling 0 pieces, a negative number of pieces, or more pieces than there are in stock is refused.
  • restock(n) adds n pieces to the stock when n is at least 1, and ignores n otherwise.
  • getStock() returns the stock. It is the only way for other code to read it.

sell and restock change the item, so they have a pointer receiver, (this *InventoryItem); getStock only reads, so a value receiver is enough. The starter code already has these headers.

Each test case creates one item with NewInventoryItem, then applies a list of operations to it, in order. The output has one line per test case: the item's Name and a colon, then what each sell and getStock call returns, separated by spaces, with sell's answer printed as True or False.

 

Example 1:

Input:
    NewInventoryItem("Pencil", 5)
    sell(3)
    getStock()
    sell(5)
    getStock()
    restock(10)
    getStock()
Output: Pencil: True 2 False 2 12
Explanation: Selling 3 of the 5 pencils works and leaves 2. Selling 5 more is refused, because only 2 are left,
and the stock stays 2. restock(10) brings it to 12.

Example 2:

Input:
    NewInventoryItem("Mug", 4)
    sell(4)
    getStock()
    sell(1)
    getStock()
Output: Mug: True 0 False 0
Explanation: Selling all 4 mugs is allowed and leaves 0. With nothing left, even one mug can't be sold.

Example 3:

Input:
    NewInventoryItem("Lamp", 2)
    sell(0)
    sell(-3)
    restock(0)
    restock(-5)
    getStock()
    restock(1)
    sell(3)
    getStock()
Output: Lamp: False False 2 True 0
Explanation: Selling 0 or -3 lamps is refused, and restocking 0 or -5 lamps is ignored, so the stock is still 2.
After restock(1) there are 3 lamps, and selling all 3 works.

 

Constraints:

  • A name is one word of English letters. 0 <= stock <= 104 when the item is created.
  • -104 <= n <= 104 in every sell and restock.
  • A test case has from 1 to 1000 operations, and at least one of them is a sell or a getStock.
STEP 1 OF 7
    Ctrl + Enter to check

    GO · OBJECT-ORIENTED PROGRAMMING - CHALLENGES

    Inventory Item in Go, explained

    Problem overview

    A shop keeps track of how many pieces of each item it has: the item's stock. The stock can never go below 0, and it changes only when pieces are sold or new ones arrive. So the stock is hidden and changed only through two methods that check the rules. The item's name has no rules to protect, so any code may read it.

    In Go, a name that starts with an uppercase letter is exported: code outside the package can use it. A name that starts with a lowercase letter is unexported: only code inside the package can. The lesson uses both on one struct: Name is exported, stock is not.

    The methods are:

    • NewInventoryItem(name, stock) returns an item with that name and stock.
    • sell(n) sells n pieces and returns true if n is at least 1 and at most the stock. Otherwise it changes nothing and returns false.
    • restock(n) adds n pieces when n is at least 1 and ignores it otherwise.
    • getStock() returns the stock.

    Approach

    Each method checks its rule before it changes anything.

    For sell, it is easier to describe the refusals than the successes. A sale is refused when n is below 1 (selling 0 or a negative number makes no sense) or above the stock (you can't sell what you don't have). The test uses ||, "or", and returns false right away, before the stock is touched, so a refused sale leaves everything as it was. Selling exactly the whole stock is allowed. Past the check, the sale goes through: lower the stock by n and return true. Because the check passed, the stock can't drop below 0.

    For restock, one if is enough: add n only when it is at least 1. A delivery of 0 or a negative number is not a delivery.

    Every method is constant time.

    Step by step

    1. Declare Name as a string, with an uppercase first letter, so other code can read it.
    2. Declare stock as an int, lowercase, so only sell and restock change it from outside.
    3. Write the constructor. It returns an item with Name: name and stock: stock. In each pair, the first is the field and the second is the parameter.
    4. Refuse bad sales. In sell, an if for n below 1 or above this.stock returns false.
    5. Complete the sale. After the if, subtract n from this.stock and return true.
    6. Write restock. If n >= 1, add it to the stock.
    7. Write getStock, which returns this.stock.

    The solution in Go

    type InventoryItem struct {
        Name string
        stock int
    }
    func NewInventoryItem(name string, stock int) InventoryItem {
        return InventoryItem{Name: name, stock: stock}
    }
    func (this *InventoryItem) sell(n int) bool {
        if n < 1 || n > this.stock {
            return false
        }
        this.stock -= n
        return true
    }
    func (this *InventoryItem) restock(n int) {
        if n >= 1 {
            this.stock += n
        }
    }
    func (this InventoryItem) getStock() int {
        return this.stock
    }
    

    Go details:

    • The first letter decides. There is no public or private keyword. Name and stock differ only in the case of their first letter.
    • Pointer receivers. sell and restock change the item, so they use (this *InventoryItem). getStock only reads, so a value receiver is enough.
    • Early return. The refusal comes first and returns, so the rest of the method needs no else. Go code often puts the failure cases at the top.
    • Short-circuit ||. If n < 1 is true, Go doesn't evaluate the second part. Both parts are simple here, so it only affects speed, never the answer.

    Why the check lives in the methods

    If the stock were exported, any code could write item.Stock = -50 and nothing would notice. Hiding the field forces every change through sell and restock, which are the only places that need to know the rules. The same reasoning explains why Name stays exported: reading the name can't break anything, and a getter method would add code without adding safety.

    Note that the restriction is by package. A test written in the same package can still reach stock. The hiding protects code that imports the package, which is how Go programs are normally split.

    Using it

    p := NewInventoryItem("Pencil", 5)
    fmt.Println(p.Name, p.sell(3), p.getStock(), p.sell(5), p.getStock())
    p.restock(10)
    fmt.Println(p.getStock())
    l := NewInventoryItem("Lamp", 2)
    fmt.Println(l.sell(0), l.sell(-3))
    l.restock(0)
    l.restock(-5)
    fmt.Println(l.getStock())
    l.restock(1)
    fmt.Println(l.sell(3), l.getStock())
    fmt.Println(l.sell(1), l.getStock())
    

    This prints:

    Pencil true 2 false 2
    12
    false false
    2
    true 0
    false 0
    

    Selling 3 of the 5 pencils works and leaves 2. Selling 5 more is refused because only 2 are left, and the stock stays 2. After restock(10) it is 12. The lamps show the refusals: selling 0 or -3 fails, restocking 0 or -5 is ignored, and the stock stays 2. After restock(1) there are 3, and selling all 3 works. With nothing left, even sell(1) is refused. The lesson's tests print the booleans as True and False; Go itself prints true and false.

    Edge cases and common mistakes

    • Selling the whole stock. n equal to this.stock is allowed. Writing n >= this.stock in the refusal would wrongly refuse it.
    • Changing the stock before checking. Subtracting first and testing afterwards leaves a refused sale with a changed stock.
    • Allowing 0 or negative sales. A negative sell would raise the stock, and a negative restock would lower it. Both checks exist to prevent that.
    • A value receiver on sell or restock. It compiles, but the stock never changes.
    • An uppercase Stock. It would be exported, and outside code could set it to -50 directly.
    Keep going with Go500+ step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free