An example of a chain of responsibility in Kotlin.
Table of contents for design patterns in Kotlin
In object-oriented design, the chain-of-responsibility pattern is a design pattern consisting of a source of command
objects and a series of processing objects.[1] Each processing object contains logic that defines the types of command
objects that it can handle; the rest are passed to the next processing object in the chain. A mechanism also exists for
adding new processing objects to the end of this chain. Thus, the chain of responsibility is an object oriented version
of the if ... else if ... else if ....... else ... endif idiom, with the benefit that the condition–action blocks can
be dynamically rearranged and reconfigured at runtime.
Wikipedia
Example:
Logger
interface Logger {
enum class LogLevel {
INFO, DEBUG, WARNING, ERROR;
companion object {
fun all() = values().toSet()
}
fun toSet() = setOf(this)
}
fun log(level: LogLevel, message: String)
}
AbstractLogger
abstract class AbstractLogger(
open val levels: Set<Logger.LogLevel>,
open var next: Logger? = null
) : Logger {
override fun log(level: Logger.LogLevel, message: String) {
next?.log(level, message)
}
fun appendNext(next: AbstractLogger): AbstractLogger {
this.next = next
return next
}
}
ConsoleLogger
class ConsoleLogger(
override val levels: Set<Logger.LogLevel>,
override var next:Logger? = null
): AbstractLogger(levels, next) {
override fun log(level: Logger.LogLevel, message: String) {
if (levels.contains(level)) {
println("Console log: $level $message")
}
super.log(level, message)
}
}
EmailLogger
class EmailLogger(
override val levels: Set<Logger.LogLevel>,
override var next:Logger? = null
): AbstractLogger(levels, next) {
override fun log(level: Logger.LogLevel, message: String) {
if (levels.contains(level)) {
println("Email log: $level $message")
}
super.log(level, message)
}
}
FileLogger
class FileLogger(
override val levels: Set<Logger.LogLevel>,
override var next:Logger? = null
): AbstractLogger(levels, next) {
override fun log(level: Logger.LogLevel, message: String) {
if (levels.contains(level)) {
println("File log: $level $message")
}
super.log(level, message)
}
}
Usage:
fun main() {
val logger = ConsoleLogger(all())
logger.appendNext(EmailLogger(LogLevel.ERROR.toSet()))
.appendNext(FileLogger(setOf(LogLevel.ERROR, LogLevel.WARNING)))
logger.log(LogLevel.INFO, "Info log")
logger.log(LogLevel.DEBUG, "Debug log")
logger.log(LogLevel.ERROR, "Error log")
logger.log(LogLevel.WARNING, "Warning log")
}
Result
Console log: INFO Info log
Console log: DEBUG Debug log
Console log: ERROR Error log
Email log: ERROR Error log
File log: ERROR Error log
Console log: WARNING Warning log
File log: WARNING Warning log