Why aren't my decorators being called?

Sorry for the example but I was trying to make it minimal and reprocible.


Let’s suppose I want to recreate The Beatles’ Dig It:

interface DigIt {
    fun digIt()
    fun sing()
}

class LennonDigIt : DigIt {   	
    override fun sing() {
        digIt()
    }
    
    override fun digIt() {    
        repeat(3) {
            println("Like a Rolling Stone!")                     
        }
    }
}

// My base decorator
abstract class DigItDigIt(private val digIt: DigIt) : DigIt {
    override fun sing() = digIt.sing()
    override fun digIt() = digIt.digIt()
}

// Adds the FBI
class FBIDigIt(digIt: DigIt) : DigItDigIt(digIt) {
    override fun digIt() {
        super.digIt()
        println("Like the FBI!")
    }
}

// And the CIA
class CIADigIt(digIt: DigIt) : DigItDigIt(digIt) {
    override fun digIt() {
        super.digIt()
        println("And the CIA!")
    }
}

fun main() {
    var d: DigIt
    d = LennonDigIt()    
    d = FBIDigIt(d)
    d = CIADigIt(d)
    
    d.sing()
}

It should print out “Like a Rolling Stone (3x), Like the FBI, And the CIA” but it’s printing only “Like a Rolling Stone (3x)”. What can I do to make sing() also call the decorators implementations?

1 Like

Your FBI and CIA classes cannot call their override of digIt, because they’re shorted by the fact that sing method directly calls the sing method of the digIt property. Change sing method of DigItDigIt like so:

override fun sing() = digIt()

Decorators only apply when the caller holds a reference to the decorator rather than the actual decorated object. This is not the case when the caller is the decorated object itself. A decorator can therefore only be used to intercept calls from an external caller.

This is not really a Kotlin specific issue, but rather an inherent property of the decorator pattern. In a way, this can be considered a feature rather than a limtation, as the purpose of the decorator pattern is not to affect the inner workings of the decorated object. I suspect that the decorator pattern might not be the right answer in this case, though I cannot say for sure without knowing your actual use case (I assume that you are working on something more realistic than the example shown). I suggest that you go back and take another look at your catalog of design patterns and find something that matches your problem more closely. Maybe Observer pattern. Or perhaps Strategy pattern combined with either Composite or Decorator (decorating the strategy rather than your main subject).

@alexis.manin, your solution (extracting sing to an interface and implementing sing in the decorator instead of in the decorated class) helped to solve half of the problem because I forgot to mention that my real decorated class contains some properties, which makes each decorator contain their own properties instead of only the decorated class properties.

This makes me strongly consider the @Varia solution (combining the Observer and Decorator patterns) and it worked like a charm. Thank you all and sorry if I made a non-Kotlin question.

If it helps someone, I’m posting the previous code modified to better represent my use case and its solution:

interface DigIt {
    val digs: MutableList<String>
    fun digIt()
    fun sing()
}

class LennonDigIt : DigIt {   	
    override val digs = mutableListOf<String>()
    override fun sing() {
        digIt()
    }
    
    override fun digIt() {    
        repeat(3) {
            digs.add("Like a Rolling Stone!")
        }
    }
}

abstract class DigItDigIt(private val digIt: DigIt) : DigIt {
    override val digs: MutableList<String>
        get() = digIt.digs
    override fun sing() = digIt.sing()
    override fun digIt() = digIt.digIt()
}

class FBIDigIt(digIt: DigIt) : DigItDigIt(digIt) {
    override fun digIt() {
        super.digIt()
        digs.add("Like the FBI!")
    }
}

class CIADigIt(digIt: DigIt) : DigItDigIt(digIt) {
    override fun digIt() {
        super.digIt()
        digs.add("And the CIA!")
    }
}

fun main() {
    var d: DigIt
    d = LennonDigIt()    
    d = FBIDigIt(d)
    d = CIADigIt(d)
    
    d.sing()
    println(d.digs)
    // prints [Like a Rolling Stone!, Like a Rolling Stone!, Like a Rolling Stone!]
    // should print [Like a Rolling Stone!, Like a Rolling Stone!, Like a Rolling Stone!, Like the FBI!, And the CIA!]
}

One of the solutions:

// Observer pattern
open class DigObserver {
    open fun onSing(digs: MutableList<String>) {}
}

interface DigIt {
    val digs: MutableList<String>
    fun sing()
}

class LennonDigIt : DigIt {
    private var observer: DigObserver? = null
    fun setObserver(observer: DigObserver) {
        this.observer = observer
    }

    override val digs: MutableList<String> = mutableListOf()
    
    override fun sing() {
        repeat(3) {
            digs.add("Like a Rolling Stone!")  
        }

        observer?.onSing(digs)
        println(digs)
        digs.clear()
    }
}

// Decorator pattern
abstract class DigObserverDecorator(private val observer: DigObserver) : DigObserver() {
    override fun onSing(digs: MutableList<String>) {
        observer.onSing(digs)
    }
}

class FBIDigIt(observer: DigObserver) : DigObserverDecorator(observer) {
    override fun onSing(digs: MutableList<String>) {
        super.onSing(digs)
        digs.add("Like the FBI!")
    }
}

class CIADigIt(observer: DigObserver) : DigObserverDecorator(observer) {
    override fun onSing(digs: MutableList<String>) {
        super.onSing(digs)
        digs.add("And the CIA!")
    }
}

fun main() {
    val d = LennonDigIt()

    var o = DigObserver()
    o = FBIDigIt(o)
    o = CIADigIt(o)
    d.setObserver(o)

    d.sing()
    // prints [Like a Rolling Stone!, Like a Rolling Stone!, Like a Rolling Stone!, Like the FBI!, And the CIA!]

}