Replace a delivery fee function with a flag per add-on by wrappers around a base fee, so a new add-on is one class, the order of wrapping is a list, and every old fee stays the same.
A delivery app prices an order with one function and a flag per add-on: a distance fee, a small-cart fee, a late-night fee and a surge. Every new add-on is another flag, every caller changes, and the order of the rules is hidden in the order of the if statements. Rebuild it as decorators: each add-on is a small object that takes any fee rule and is itself a fee rule.
The behaviour to keep, exactly (an Order has km, cart in rupees and hour from 0 to 23):
| Rule | Value |
|---|---|
| Distance fee | 20 plus 10 for every km |
| Small cart | adds 15 when the cart is under 199 |
| Late night | adds 20 from 11 pm to 5 am: hour >= 23 or hour < 5 |
| Surge | multiplies the fee by a whole number (the lesson uses 2) |
Build this API (names are Python's; JavaScript and TypeScript use the same names):
DistanceFee(), with fee(order).SmallCart(inner), LateNight(inner) and Surge(inner, times): each has fee(order), calls inner.fee(order) and then applies its own rule to the answer. They work with any object that has fee(order).build(names): starts from a DistanceFee and wraps once per name, in list order, so the first name sits closest to the base and the last is outermost. The names are "small-cart", "late-night" and "surge" (surge uses 2). Any other name raises ValueError (JavaScript and TypeScript: an Error) with the message unknown add-on: <name>.The tests check every old number, that wrapping order changes the answer (210 against 175), that a decorator accepts another decorator, that a new add-on written inside a test plugs in without editing anything, that fee never changes its order or its inner rule, and that no flag is left in the code.
Write your code, then run 10 example tests and 5 hidden tests. Each run is limited to 8 seconds: an infinite loop gets stopped and the runtime restarts, your code stays.