Problem overview
A shop tracks how many pieces of each item it has. 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 through two methods that check the rules. The name has no rules to protect, so it stays readable by any code.
InventoryItem(name, initialStock)creates an item. The name is a readable field; the privatestockstarts atinitialStock.sell(n)sellsnpieces ifnis at least 1 and at most the stock, lowering the stock and returningtrue. Otherwise it changes nothing and returnsfalse.restock(n)addsnpieces whennis at least 1 and ignores it otherwise. It returns nothing.getStock()returns the stock.
With 5 pencils in stock, sell(3) works and leaves 2. Then sell(5) is refused, because only 2 are left. restock(10) brings the stock to 12.
Approach: check first, change after
A sale is valid only when n is between 1 and the stock, both included. So sell first rejects what is invalid: n below 1 (zero or negative) or n above the stock. Only a request that passes the check gets to change the stock, so a refused sale leaves it untouched and the stock never goes below 0. Selling exactly the whole stock is allowed.
Step by step
- Declare the private field. The header has
val name, which any code can read, and a plain parameterinitialStock. Below the header,private var stock: Int = initialStockcreates the field. It is avarbecause the methods change it. - Refuse invalid sales. In
sell, anifwithn < 1 || n > stockreturnsfalse. The||means "or". - Complete a valid sale. After the
if, subtractnfromstockand returntrue. restockaddsntostockonly whenn >= 1. Zero or a negative number isn't a delivery.getStockreturnsstock.
The solution in Scala
class InventoryItem(val name: String, initialStock: Int) {
private var stock: Int = initialStock
def sell(n: Int): Boolean = {
if (n < 1 || n > stock) {
return false
}
stock -= n
return true
}
def restock(n: Int): Unit = {
if (n >= 1) {
stock += n
}
}
def getStock(): Int = {
return stock
}
}
Scala details:
- Constructor parameters and fields.
val namein the header creates a public read-only field.initialStockhas noval, so it is only a constructor parameter, not a field; its value is copied into the privatestock. privateis checked by the compiler. Outside the class,item.stockis rejected: "private variable stock can only be accessed from class InventoryItem."- An early
returnends the method at once, so the lines after theifrun only for a valid sale. Unitis the result type ofrestock, which returns nothing useful.
Using the class
val p = new InventoryItem("Pencil", 5)
println(p.name)
println(p.sell(3))
println(p.getStock())
println(p.sell(5))
println(p.getStock())
p.restock(10)
println(p.getStock())
It prints:
Pencil
true
2
false
2
12
The tests print the booleans as True and False; Scala itself prints true and false.
The invalid requests all leave the stock alone. For an item l created with 2 lamps, l.sell(0) and l.sell(-3) both return false, and l.restock(0) and l.restock(-5) do nothing, so l.getStock() is still 2. After l.restock(1), l.sell(3) returns true and the stock is 0.
Public name, private stock
The two fields are treated differently on purpose. The name has no rule to protect, so val name in the header is a public, read-only field, and any code can write item.name. The stock has a rule (never below 0, changed only by valid sales and deliveries), so it is private and every change goes through sell or restock.
The return value of sell matters. A refused sale is not an error that stops the program: the method answers false and the caller decides what to do. A sale that goes through answers true. This lets the caller tell the two cases apart without reading the stock before and after.
Selling the exact stock is a good test of the boundary. An item with 4 mugs allows sell(4), which returns true and leaves 0. After that, even sell(1) is refused, because 1 is above the stock of 0.
Complexity
Each method does a couple of comparisons and at most one update, so each runs in O(1) time and the object stores a name and one integer.
Testing
- Pencil, 5 in stock:
sell(3),getStock(),sell(5),getStock(),restock(10),getStock()give true, 2, false, 2 and 12. - Lamp, 2 in stock:
sell(0),sell(-3),restock(0),restock(-5),getStock(),restock(1),sell(3),getStock()give false, false, 2, true and 0.
The lamp case covers all four ways to be refused or ignored: selling 0, selling a negative number, restocking 0 and restocking a negative number.
Common mistakes
- Using
n >= stockas the refusal test. Selling the whole stock is allowed, so onlyn > stockis refused. - Forgetting
n < 1. A negative sale would raise the stock, andsell(0)would returntruewhen it should be refused. - Changing the stock before checking. The check must come first, or a refused sale would still change it.
- Accepting negative restocks.
restock(-5)would lower the stock behind the back ofsell's rules.
