Problem overview
A shop tracks 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 property, changed only through two methods that check the rules. The item's name has no rules to protect, so it stays a property that any code can read.
The class InventoryItem has:
InventoryItem(name, stock), which creates an item with that name and that many pieces in stock;sell(_ n:), which sellsnpieces. Ifnis at least 1 and at most the stock, it lowers the stock bynand returnstrue. Otherwise it changes nothing and returnsfalse: selling 0 pieces, a negative number, or more than there are is refused;restock(_ n:), which addsnpieces whennis at least 1 and ignores it otherwise, returning nothing;getStock(), which returns the stock and is the only way for other code to read it.
For a pencil item with stock 5: sell(3) returns true and leaves 2. Then sell(5) is refused because only 2 are left, and the stock stays 2. After restock(10) the stock is 12. The lesson's tests print the booleans as True and False.
Approach
Each method guards the stock before touching it. A sale is valid only when 1 <= n <= stock, so the method first checks the opposite, n < 1 || n > stock, and returns false straight away. Everything after that check can assume the sale is valid. This "refuse early" shape keeps a refused sale from changing anything, and because the stock only ever drops by at most itself, it can't go below 0.
Restocking is simpler: add n only if it is at least 1. All methods are O(1).
Step by step
- Declare the name as
let name: String. It isletbecause it never changes, and it has no access modifier, so other code can read it. - Declare the stock as
private var stock: Int. Outside the class,item.stockdoesn't compile. - Store both parameters in
init, withself.name = nameandself.stock = stock. - In
sell, refuse bad requests first:if n < 1 || n > stockreturnsfalse. The comparison withstockis strict: selling exactly the stock is allowed. - Complete the sale:
stock -= n, thenreturn true. - In
restock, anif n >= 1addsnto the stock. Zero or a negative number isn't a delivery. getStockreturnsstock.
The solution in Swift
class InventoryItem {
let name: String
private var stock: Int
init(_ name: String, _ stock: Int) {
self.name = name
self.stock = stock
}
func sell(_ n: Int) -> Bool {
if n < 1 || n > stock {
return false
}
stock -= n
return true
}
func restock(_ n: Int) {
if n >= 1 {
stock += n
}
}
func getStock() -> Int {
return stock
}
}
Using it:
let p = InventoryItem("Pencil", 5)
print(p.name)
print(p.sell(3))
print(p.getStock())
print(p.sell(5))
print(p.getStock())
p.restock(10)
print(p.getStock())
let l = InventoryItem("Lamp", 2)
print(l.sell(0), l.sell(-3))
l.restock(0); l.restock(-5)
print(l.getStock())
l.restock(1)
print(l.sell(3), l.getStock())
Pencil
true
2
false
2
12
false false
2
true 0
Swift details worth knowing
- Reading the name works, reading the stock doesn't.
p.nameis fine. Writingp.stock = 500outside the class is a compile error:'stock' is inaccessible due to 'private' protection level. letversusvar. The name isletand is set once ininit. The stock must change, so it isvar.- The parameter and the property share a name. In
init,stockis the parameter andself.stockis the property. Insidesell, there is no parameter calledstock, sostockmeans the property. ||is "or". The conditionn < 1 || n > stockis true if either part is true.- A method with no return type, like
restock, returns nothing, so the call is a statement on its own line.
Edge cases and common mistakes
- Selling exactly the whole stock. A mug item with stock 4 and
sell(4)returnstrueand leaves 0. The nextsell(1)returnsfalse, and the stock stays 0. Usingn >= stockas the refusal would wrongly block the first sale. - Selling 0 or a negative number. Both are refused. Without the
n < 1part,sell(-3)would "sell" minus three pieces and raise the stock. - Changing the stock before checking. Subtracting first and testing afterwards leaves a refused sale half done.
- Restocking 0 or less. It must be ignored. Without the
if,restock(-5)would lower the stock. - Making the stock public would pass many tests but give up the guarantee that it never goes below 0.
Why return a Bool from sell?
A sale can fail, and the caller needs to know. Returning true or false lets a shop's code decide what to do next: tell the customer there isn't enough, or offer fewer pieces. The method makes the decision, the caller reacts to it, and in both cases the stock stays valid. restock returns nothing because it has nothing to report: an ignored delivery just changes nothing.
Also notice that the name is a let: even though other code can read it, nobody can assign a new one, so a read-only property needs no private to be safe.
Complexity
Each method compares and adds or subtracts, so it runs in O(1) time. An item stores a name and one integer, which is O(1) beyond the name.
Testing
- Pencil with 5:
sell(3)gives true, stock 2,sell(5)gives false, stock 2,restock(10), stock 12. - Mug with 4:
sell(4)gives true, stock 0,sell(1)gives false, stock 0. - Lamp with 2:
sell(0)andsell(-3)give false,restock(0)andrestock(-5)do nothing, stock 2. Afterrestock(1),sell(3)gives true and the stock is 0.
