Motivation: A class has two independent dimensions of variation (e.g., shape and colour), and combining them via inheritance would multiply the number of subclasses.
Intent: Separate an abstraction from its implementation so that the two can vary independently.
The point of this is to decouple 2 dimensions of variations so they can evolve independently, connected by a composition rather than inheritance. This one is slightly more confusing but the idea here, is that in this example, we have a Circle and Square which can both be rendered as a vector and raster. Instead of each class having a VectorSquare and RasterSquare etc, the renderer is pulled out.
interface Renderer {
fun renderCircle(radius: Double)
fun renderSquare(side: Double)
}
class VectorRenderer : Renderer {
override fun renderCircle(radius: Double) { /* draw with vectors */ }
override fun renderSquare(side: Double) { /* draw with vectors */ }
}
class RasterRenderer : Renderer {
override fun renderCircle(radius: Double) { /* draw with pixels */ }
override fun renderSquare(side: Double) { /* draw with pixels */ }
}
abstract class Shape(protected val renderer: Renderer) {
abstract fun draw()
}
class Circle(renderer: Renderer, val radius: Double) : Shape(renderer) {
override fun draw() = renderer.renderCircle(radius)
}
class Square(renderer: Renderer, val side: Double) : Shape(renderer) {
override fun draw() = renderer.renderSquare(side)
}