Implement interchangeable study policies
Build a planner using composition. The planner asks a policy for a session duration; it should not inspect concrete implementation types.
interface StudyPolicy { fun minutes(): Int }
class ShortSession : StudyPolicy { override fun minutes() = 15 }
class DeepSession : StudyPolicy { override fun minutes() = 50 }
class Planner(private val policy: StudyPolicy) {
fun plan(topic: String): String {
require(topic.isNotBlank())
return "$topic: ${policy.minutes()} min"
}
}
This is the strategy pattern expressed with a small interface. A function parameter can replace the interface when there is only one operation and no additional contract. The useful property is replaceable behavior, not the pattern name.
Acceptance checks
Verify both policies, reject a blank topic, and introduce a fake policy returning a known duration. Decide where positive-duration validation belongs. Add a policy without changing Planner.
Extension: return a data class instead of a formatted string so scheduling and display stay separate. Explain when an interface with one implementation adds unnecessary indirection, and when upcoming alternative implementations justify it.