Skip to content

Instantly share code, notes, and snippets.

@joelburton
Last active September 18, 2021 22:49
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save joelburton/461fa29a90f26b154b1f5fa9ca1aee80 to your computer and use it in GitHub Desktop.
Save joelburton/461fa29a90f26b154b1f5fa9ca1aee80 to your computer and use it in GitHub Desktop.
OMG Delegation in Kotlin
/** Single abstract-method interface for a way to fly. */
fun interface FlyBehavior {
fun fly()
}
// implementations of the single-method interface
val FlyWithWings = FlyBehavior { println("I'm flying!") }
val FlyNoWay = FlyBehavior { println("No way") }
val FlyRocketPowered = FlyBehavior { println("Rocket!!!!!!") }
/** Single abstract-method interface for a way to quack. */
fun interface QuackBehavior {
fun quack()
// there can be other methods --- they just can't be abstract
fun doubleQuack() {
quack()
quack()
}
}
// implementations of the single-method interface
val Quack = QuackBehavior { println("Quack!") }
val MuteQuack = QuackBehavior { println("<< silence >>") }
val Squeak = QuackBehavior { println("Squeak!") }
/** Base Duck class.
*
* Ducks can fly and quack, but the ways they do so can vary. Rather than
* having child classes implement fly and quack themselves via classical
* inheritance, our ducks delegate to Fly and Quack strategies automatically.
* You can make an instance of this class quack like:
*
* someDuck.quack()
*/
abstract class Duck(quack: QuackBehavior, fly: FlyBehavior) :
QuackBehavior by quack,
FlyBehavior by fly {
abstract fun display()
fun swim() = println("All ducks swim!")
}
/** Mallard duck.
*
* Defaults to regular quack and flying, but individual instances can
* have different strategies they delegate to.
*/
class MallardDuck(
quack: QuackBehavior = Quack,
fly: FlyBehavior = FlyWithWings,
) : Duck(quack, fly) {
override fun display() = println("I'm a Mallard")
}
/** Model duck.
*
* These delegate to the Squeak and FlyNoWay strategies, but it's not
* configurable for model ducks.
*/
class ModelDuck : Duck(Squeak, FlyNoWay) {
override fun display() = println("Just a model")
}
fun main() {
// a regular mallard with the normal delegation
val mallard = MallardDuck()
mallard.quack()
mallard.doubleQuack()
mallard.fly()
mallard.display()
// a special mallard with different delegation
val fastSilentMallard = MallardDuck(MuteQuack, FlyRocketPowered)
fastSilentMallard.quack()
fastSilentMallard.fly()
fastSilentMallard.display()
// a model duck that cannot choose its delegation
val model = ModelDuck()
model.quack()
model.fly()
model.display()
model.swim()
}
@joelburton
Copy link
Author

joelburton commented Sep 18, 2021

(the main takeaway here is in line 76:

    val fastSilentMallard = MallardDuck(MuteQuack, FlyRocketPowered)

— we're able to compose together features without defining a class for that combination, and thereby avoid n x m class definitions.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment