Prepping for a technical interview by designing a ride cost estimation system. The problem: base fare, premium fare, surge pricing โ all calculating cost differently but accepting the same input. Classic Strategy territory.
- When the goal is fixed but the algorithm changes, reach for Strategy โ it replaces a long if/else chain with a family of interchangeable classes that all implement the same interface
- The interface has one job: declare what a strategy does, not how โ every concrete strategy fills in the how independently
interface CostStrategy {
fun calculate(trip: Trip): Double
}
class BaseFareStrategy : CostStrategy {
override fun calculate(trip: Trip): Double {
return 2.0 + (trip.distance * 1.5) + (trip.duration * 0.25)
}
}
class PremiumStrategy : CostStrategy {
override fun calculate(trip: Trip): Double {
return BaseFareStrategy().calculate(trip) * 1.5
}
}- Adding a Decorator on top of Strategy is where things get interesting โ
SurgePricingStrategywraps any existing strategy and multiplies the result, so surge can stack on top of base or premium without either one knowing
class SurgePricingStrategy(
private val wrapped: CostStrategy,
private val multiplier: Double
) : CostStrategy {
override fun calculate(trip: Trip): Double {
return wrapped.calculate(trip) * multiplier
}
}- The Builder pattern solves the "multiple surges at once" problem cleanly โ instead of nesting constructors manually, chain the conditions and let
build()return the composed strategy
class SurgeStrategyBuilder(private var strategy: CostStrategy) {
fun applyPeakHour(isPeakHour: Boolean): SurgeStrategyBuilder {
if (isPeakHour) strategy = SurgePricingStrategy(strategy, 1.5)
return this
}
fun applyHeavyWeather(isHeavyWeather: Boolean): SurgeStrategyBuilder {
if (isHeavyWeather) strategy = SurgePricingStrategy(strategy, 2.0)
return this
}
fun build(): CostStrategy = strategy
}- Open/Closed Principle shows up here without trying โ adding a new pricing model means writing a new class, not editing the ones that already work
- Strategy, Decorator, Builder, and Factory all played a role in one problem; they're not alternatives, they're layers that each solve a different dimension of the same design