[코틀린] Design Pattern - Behavioral

Design Pattern - Behavioral

Behavioral Patterns

ref : https://github.com/dbacinski/Design-Patterns-In-Kotlin

Project maintained by @dbacinski (Dariusz Baciński)

1. Observer / Listener

The observer pattern is used to allow an object to publish changes to its state. Other objects subscribe to be immediately notified of any changes.

Example

interface TextChangedListener {
	fun onTextChanged(oldText:String, newText: String)
}

class PrintingTextChangedListener : TextChangedListener {
	private var text = ""
	
	override fun onTextChanged(oldText:String, newText: String) {
		text = "Text is changed: $oldText -> $newText"
	}
}

class TextView {
	val listeners = mutableListOf<TextChangedListener>()
	var text = String by Delegates.observable("<empty>") { _, old, new ->
		listeners.forEach {it.onTextChanged(old, new) }
	}
}

Usage

val textView = TextView().apply{
	listener = PrintingTextChangedListener()
}

with(textView) {
	text = "Lorem ipsum"
	text = "dolor sit amet"
]

Output

Text is changed <empty> -> Lorem ipsum
Text is changed Lorem ipsum -> dolor sit amet

2. Strategy

The strategy pattern is used to create an interchangeable family of algorithms from which the required process is chosen at run-time.

Example

class Printer(private val stringFormatterStrategy: (String) -> String){
	fun printString(str: String){
		println(stringFormatterStrategy(str))
	}
}

val lowerCaseFormatter: (String) -> String = {it.toLowerCase()}
val upperCaseFormatter = {it: String -> it.toUpperCase()}

Usage

val inputString = "LOREM ipsum DOLOR sit amet"

val lowerCasePrinter = Printer(lowerCaseFormatter)
lowerCasePrinter.printString(inputString)

val upperCasePrinter = Printer(upperCaseFormatter)
upperCasePrinter.printString(inputString)

val prefixPrinter = Printer{"Prefix: $it"}
prefixPrinter.printString(inputString)

Output

lorem ipsum dolor sit amet
LOREM IPSUM DOLOR SIT AMET
Prefix: LOREM ipsum DOLOR sit amet

3. Command

The command pattern is used to express a request, including the call to be made and all of its required parameters, in a command object. The command may then be executed immediately or held for later use.

Example

interface OrderCommand {
    fun execute()
}

class OrderAddCommand(val id: Long) : OrderCommand {
    override fun execute() = println("Adding order with id: $id")
}

class OrderPayCommand(val id: Long) : OrderCommand {
    override fun execute() = println("Paying for order with id: $id")
}

class CommandProcessor {
    private val queue = ArrayList<OrderCommand>()
    
    fun addToQueue(orderCommand: OrderCommand): CommandProcessor =
    	apply {
            queue.add(orderCommand)
        }
    fun processCommands(): CommandProcessor =
    	apply {
            queue.forEach {	it.execute() }
            queue.clear()
        }
}

Usage

CommandProcessor()
	.addToQueue(OrderAddCommand(1L))
	.addToQueue(OrderAddCommand(2L))
	.addToQueue(OrderPayCommand(2L))
	.addToQueue(OrderPayCommand(1L))
	.processCommands()

Output

Adding order with id: 1
Adding order with id: 2
Paying for order with id: 2
Paying for order with id: 1

4. State

Example

sealed class AuthorizationState

object Unauthorized : AuthorizationState()

class Authorized(val userName: String) : AuthorizationState()

class AuthorizationPresenter{
    private var state: AuthorizationState = Unauthorized
    
    val isAuthorized: Boolean
    	get() = when (state) {
            is Authorized -> true
            is UnAuthorized -> false
        }
    
    val userName: String
    	get() {
            val state = this.state //val enables smart casting of state
            return when (state)  {
                is Authorized -> state.userName
                is Unauthorized -> "Unknown"
            }
        }
    fun loginUser(userName: String) {
        state = Authorized(userName)
    }
    
    fun logoutUser() {
        state = Unauthorized
    }
    
    override fun toString() = "User '$userName' is logged in: $isAuthorized"
}

Usage

val authoirizationPresenter = AuthorizationPresenter()

authorizationPresenter.loginUser("admin")
println(authorizationPresenter)

authorizationPresenter.logoutUser()
println(authorizationPresenter)

Output

User 'admin' is logged in: true
User 'Unknown' is logged in: false