Last active
September 18, 2021 22:49
-
-
Save joelburton/461fa29a90f26b154b1f5fa9ca1aee80 to your computer and use it in GitHub Desktop.
OMG Delegation in Kotlin
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
/** 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() | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
(the main takeaway here is in line 76:
— we're able to compose together features without defining a class for that combination, and thereby avoid n x m class definitions.