← → or space · progress saves for Continue on the roadmap
Goal
Swap how a cart is priced without editing the cart class every time marketing adds a rule.
Overlap
- Same structural idea as Open/closed (Level 13). Here the focus is runtime selection of rules.
Step 1 - Strategy interface
abstract class DiscountStrategy {
double discount(double subtotal);
}
class NoDiscount implements DiscountStrategy {
@override
double discount(double subtotal) => 0;
}
class PercentOff implements DiscountStrategy {
final double rate;
PercentOff(this.rate);
@override
double discount(double subtotal) => subtotal * rate;
}
class FixedOff implements DiscountStrategy {
final double amount;
FixedOff(this.amount);
@override
double discount(double subtotal) {
final d = amount;
return d > subtotal ? subtotal : d;
}
}Step 2 - Context
class Cart {
final List<double> linePrices;
DiscountStrategy strategy;
Cart({required this.linePrices, required this.strategy});
double subtotal() => linePrices.fold<double>(0, (a, b) => a + b);
double total() {
final s = subtotal();
final d = strategy.discount(s);
return s - d;
}
}
void main() {
final cart = Cart(
linePrices: [10, 20],
strategy: PercentOff(0.1),
);
print(cart.total());
cart.strategy = FixedOff(25);
print(cart.total());
}Practice tasks
- Add
class MemberTuesday implements DiscountStrategywith a simple rule you invent. - Load strategy from a string config (
none,percent:0.15,fixed:5) in a tiny parser function. - Ensure
discountnever returns negative or NaN; clamp in strategies or inCart.total.