Last active
June 14, 2022 14:19
-
-
Save redhead/9ed3b48d50843f16ac76b18d08c07e99 to your computer and use it in GitHub Desktop.
Kotlin Context Receivers allow nice Feature Toggles
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// client code | |
object NewFeature : Feature | |
object NewFeatureVariant : Feature | |
fun main() { | |
// configuration | |
val featureConfiguration = mapOf( | |
NewFeature to true, | |
NewFeatureVariant to true | |
) | |
val featureFlagContext = object : FeatureFlagContext { | |
override fun isFeatureEnabled(feature: Feature): Boolean { | |
return featureConfiguration[feature] ?: false | |
} | |
} | |
// service bean creation (could be a @Bean method) | |
val serviceA = | |
with(featureFlagContext) { // provide the context | |
createServiceA() | |
} | |
// invocation | |
serviceA.doAStuff() | |
} | |
context(FeatureFlagContext) | |
fun createServiceA(): A { | |
val newA = NewA() | |
val oldA = OldA() | |
return object : A { | |
override fun doAStuff(): Boolean { | |
return whenFeatureOnOrElse( | |
NewFeature, | |
{ newA.doAStuff() }, | |
{ oldA.doAStuff() } | |
) | |
} | |
} | |
} | |
interface A { | |
fun doAStuff(): Boolean | |
} | |
context(FeatureFlagContext) | |
class NewA : A { | |
override fun doAStuff(): Boolean { | |
if (NewFeatureVariant.isEnabled()) { | |
println("Feature variant 2 enabled") | |
} else { | |
println("Feature variant 1 enabled") | |
} | |
return true | |
} | |
} | |
class OldA : A { | |
override fun doAStuff(): Boolean { | |
println("Old impl") | |
return false | |
} | |
} | |
// infrastructure code | |
interface Feature { | |
val name: String | |
get() = this.javaClass.simpleName | |
} | |
interface FeatureFlagContext { | |
fun isFeatureEnabled(feature: Feature): Boolean | |
} | |
// utility method to switch behavior on feature on/off | |
context(FeatureFlagContext) | |
fun <T> whenFeatureOnOrElse(feature: Feature, bodyEnabled: () -> T, bodyDisabled: () -> T): T { | |
return when { | |
isFeatureEnabled(feature) -> bodyEnabled() | |
else -> bodyDisabled() | |
} | |
} | |
// utility method to nicely get feature state | |
context(FeatureFlagContext) | |
fun <T : Feature> T.isEnabled(): Boolean { | |
return isFeatureEnabled(this) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment