Card 07/ 08
Practice: Adding a Shipping Carrier Without Editing the Calculator
Two carriers in an if chain, and a third arriving next sprint.
Making code open for a new case starts with finding the code that every new case forces you to edit. This exercise gives you a shipping calculator with two carriers. A third, Evri, at 2.90 plus 1.00 per kilogram, arrives next sprint, and more are expected. Restructure the code so that adding Evri is a new class and nothing else.
def shipping_cost(carrier, weight_kg):
if carrier == "royal-mail":
return 3.50 + 0.80 * weight_kg
elif carrier == "dpd":
return 6.00 + 0.50 * weight_kg
raise ValueError(f"unknown carrier {carrier}")
for carrier in ["royal-mail", "dpd"]:
print(f"{carrier}: {shipping_cost(carrier, 4):.2f}")royal-mail: 6.70
dpd: 8.00Answer two questions before you write anything. Which lines would you edit to add Evri today? What method would every carrier need, so that shipping_cost never has to ask which carrier it has?
Show one good answer
Today, adding Evri means editing shipping_cost itself: a new elif before the raise. The varying part is the cost calculation, so each carrier becomes a class with a cost(weight_kg) method, and shipping_cost calls that method on whatever carrier it is given.
class RoyalMail:
def cost(self, weight_kg):
return 3.50 + 0.80 * weight_kg
class Dpd:
def cost(self, weight_kg):
return 6.00 + 0.50 * weight_kg
class Evri:
def cost(self, weight_kg):
return 2.90 + 1.00 * weight_kg
def shipping_cost(carrier, weight_kg):
return carrier.cost(weight_kg)
for carrier in [RoyalMail(), Dpd(), Evri()]:
print(f"{type(carrier).__name__}: {shipping_cost(carrier, 4):.2f}")RoyalMail: 6.70
Dpd: 8.00
Evri: 6.90Royal Mail and DPD print the same costs as before, 6.70 and 8.00 for 4 kilograms, which is the check that the refactoring changed nothing. Evri's 6.90 comes from a class added afterwards. Because Python is duck typed, shipping_cost needs no declared interface to work; in Java or TypeScript you would add a Carrier interface with cost.
← → move through sections and cards