Behavioral Design Patterns - Kotlin Flashcards

1
Q

Behavioral Design Patterns

A

In software engineering, behavioral design patterns are design patterns that identify common communication patterns between objects and realize these patterns. By doing so, these patterns increase flexibility in carrying out this communication.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
2
Q

Observer / Listener Pattern

A

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.

“Define a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically”.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
3
Q

Observer / Listener Pattern Example

A

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()

var text: String by Delegates.observable(“”) { _, 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 -> Lorem ipsum
Text is changed Lorem ipsum -> dolor sit amet

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
4
Q

Strategy Pattern

A

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

In the Strategy pattern, a class behaviour or its algorithm can be changed at run time. This type of design pattern comes under behaviour pattern.

In the Strategy pattern, we create objects which represent various strategies and a context object whose behaviour varies as per its strategy object. The strategy object changes the executing algorithm of the context object.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
5
Q

Strategy Pattern Example

A

Example :

class Printer(private val stringFormatterStrategy: (String) -> String) {

fun printString(string: String) {
 println(stringFormatterStrategy(string))
 }
}

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

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
6
Q

Command Pattern

A

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.

The Command design pattern is used to encapsulate a request as an object and pass to an invoker, wherein the invoker does not know how to service the request but uses the encapsulated command to perform an action.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
7
Q

Command Pattern Example

A

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()

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

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
8
Q

State Pattern

A

The state pattern is used to alter the behaviour of an object as its internal state changes. The pattern allows the class for an object to apparently change at run-time.

The State design pattern is used when an Object changes its behaviour based on its internal state.

If we have to change the behaviour of an object based on its state, we can have a state variable in the Object and use if-else condition block to perform different actions based on the state.

State pattern is used to provide a systematic and lose-coupled way to achieve this through Context and State implementations.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
9
Q

State Pattern Example

A

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 authorizationPresenter = AuthorizationPresenter()

authorizationPresenter.loginUser(“admin”)
println(authorizationPresenter)

authorizationPresenter.logoutUser()
println(authorizationPresenter)

Output :

User ‘admin’ is logged in: true
User ‘Unknown’ is logged in: false

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
10
Q

Chain of Responsibility Pattern

A

The chain of responsibility pattern is used to process varied requests, each of which may be dealt with by a different handler.

Chain of responsibility pattern is used to achieve loose coupling in software design where a request from the client is passed to a chain of objects to process them. Later, the object in the chain will decide themselves who will be processing the request and whether the request is required to be sent to the next object in the chain or not.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
11
Q

Chain of Responsibility Pattern Example

A

Example :

interface HeadersChain {
 fun addHeader(inputHeader: String): String
}

class AuthenticationHeader(val token: String?, var next: HeadersChain? = null) : HeadersChain {

override fun addHeader(inputHeader: String): String {
token ?: throw IllegalStateException(“Token should be not null”)
return inputHeader + “Authorization: Bearer $token\n”
.let { next?.addHeader(it) ?: it }
}
}

class ContentTypeHeader(val contentType: String, var next: HeadersChain? = null) : HeadersChain {

override fun addHeader(inputHeader: String): String =
inputHeader + “ContentType: $contentType\n”
.let { next?.addHeader(it) ?: it }
}

class BodyPayload(val body: String, var next: HeadersChain? = null) : HeadersChain {

override fun addHeader(inputHeader: String): String =
inputHeader + “$body”
.let { next?.addHeader(it) ?: it }
}

Usage :

//create chain elements
val authenticationHeader = AuthenticationHeader("123456")
val contentTypeHeader = ContentTypeHeader("json")
val messageBody = BodyPayload("Body:\n{\n\"username\"=\"dbacinski\"\n}")
//construct chain
authenticationHeader.next = contentTypeHeader
contentTypeHeader.next = messageBody

//execute chain
val messageWithAuthentication =
authenticationHeader.addHeader(“Headers with Authentication:\n”)
println(messageWithAuthentication)

val messageWithoutAuth =
contentTypeHeader.addHeader(“Headers:\n”)
println(messageWithoutAuth)

Output :

Headers with Authentication:
Authorization: Bearer 123456
ContentType: json
Body:
{
“username”=”dbacinski”
}

Headers:
ContentType: json
Body:
{
“username”=”dbacinski”
}

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
12
Q

Visitor Pattern

A

The visitor pattern is used to separate a relatively complex set of structured data classes from the functionality that may be performed upon the data that they hold.

The Visitor pattern is used when we have to perform an operation on a group of similar kind of Objects.

With the help of the visitor pattern, we can move the operational logic from the objects to another class.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
13
Q

Visitor Pattern Example

A

Example :

interface ReportVisitable {
 fun accept(visitor: ReportVisitor): R
}
class FixedPriceContract(val costPerYear: Long) : ReportVisitable {
 override fun accept(visitor: ReportVisitor): R = visitor.visit(this)
}
class TimeAndMaterialsContract(val costPerHour: Long, val hours: Long) : ReportVisitable {
 override fun accept(visitor: ReportVisitor): R = visitor.visit(this)
}
class SupportContract(val costPerMonth: Long) : ReportVisitable {
 override fun accept(visitor: ReportVisitor): R = visitor.visit(this)
}

interface ReportVisitor {

fun visit(contract: FixedPriceContract): R
 fun visit(contract: TimeAndMaterialsContract): R
 fun visit(contract: SupportContract): R
}

class MonthlyCostReportVisitor : ReportVisitor {

override fun visit(contract: FixedPriceContract): Long =
contract.costPerYear / 12

override fun visit(contract: TimeAndMaterialsContract): Long =
contract.costPerHour * contract.hours

override fun visit(contract: SupportContract): Long =
contract.costPerMonth
}

class YearlyReportVisitor : ReportVisitor {

override fun visit(contract: FixedPriceContract): Long =
contract.costPerYear

override fun visit(contract: TimeAndMaterialsContract): Long =
contract.costPerHour * contract.hours

override fun visit(contract: SupportContract): Long =
contract.costPerMonth * 12
}

Usage :

val projectAlpha = FixedPriceContract(costPerYear = 10000)
val projectGamma = TimeAndMaterialsContract(hours = 150, costPerHour = 10)
val projectBeta = SupportContract(costPerMonth = 500)
val projectKappa = TimeAndMaterialsContract(hours = 50, costPerHour = 50)

val projects = arrayOf(projectAlpha, projectBeta, projectGamma, projectKappa)

val monthlyCostReportVisitor = MonthlyCostReportVisitor()

val monthlyCost = projects.map { it.accept(monthlyCostReportVisitor) }.sum()
println(“Monthly cost: $monthlyCost”)
assertThat(monthlyCost).isEqualTo(5333)

val yearlyReportVisitor = YearlyReportVisitor()
val yearlyCost = projects.map { it.accept(yearlyReportVisitor) }.sum()
println(“Yearly cost: $yearlyCost”)
assertThat(yearlyCost).isEqualTo(20000)

Output :

Monthly cost: 5333
Yearly cost: 20000

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
14
Q

Mediator Pattern

A

Mediator design pattern is used to provide a centralized communication medium between different objects in a system. This pattern is very helpful in an enterprise application where multiple objects are interacting with each other.

“Mediator pattern is used to reduce communication complexity between multiple objects or classes”.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
15
Q

Mediator Pattern Example

A

Example :

class ChatUser(private val mediator: ChatMediator, val name: String) {
 fun send(msg: String) {
 println("$name: Sending Message= $msg")
 mediator.sendMessage(msg, this)
 }
fun receive(msg: String) {
 println("$name: Message received: $msg")
 }
}

class ChatMediator {

private val users: MutableList = ArrayList()

fun sendMessage(msg: String, user: ChatUser) {
users
.filter { it != user }
.forEach {
it.receive(msg)
}
}

fun addUser(user: ChatUser): ChatMediator =
 apply { users.add(user) }

}

Usage :

val mediator = ChatMediator()
val john = ChatUser(mediator, “John”)

mediator
.addUser(ChatUser(mediator, “Alice”))
.addUser(ChatUser(mediator, “Bob”))
.addUser(john)
john.send(“Hi everyone!”)

Output :

John: Sending Message= Hi everyone!
Alice: Message received: Hi everyone!
Bob: Message received: Hi everyone!

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
16
Q

Memento Pattern

A

The memento pattern is a software design pattern that provides the ability to restore an object to its previous state (undo via rollback).

Memento pattern is used to restore the state of an object to a previous state. As your application is progressing, you may want to save checkpoints in your application and restore back to those checkpoints later.

The purpose of the memento design pattern is to provide the ability to execute an undo action in order to restore an object to a previous state. The Memento pattern is also known as Token.

17
Q

Memento Pattern Example

A

Example :

data class Memento(val state: String)

class Originator(var state: String) {

fun createMemento(): Memento {
 return Memento(state)
 }
fun restore(memento: Memento) {
 state = memento.state
 }
}
class CareTaker {
 private val mementoList = ArrayList()
fun saveState(state: Memento) {
 mementoList.add(state)
 }
fun restore(index: Int): Memento {
 return mementoList[index]
 }
}

Usage :

val originator = Originator(“initial state”)
val careTaker = CareTaker()
careTaker.saveState(originator.createMemento())

originator.state = “State #1”
originator.state = “State #2”
careTaker.saveState(originator.createMemento())

originator.state = “State #3”
println(“Current State: “ + originator.state)
assertThat(originator.state).isEqualTo(“State #3”)

originator.restore(careTaker.restore(1))
println(“Second saved state: “ + originator.state)
assertThat(originator.state).isEqualTo(“State #2”)

originator.restore(careTaker.restore(0))
println(“First saved state: “ + originator.state)

Output :

Current State: State #3
Second saved state: State #2
First saved state: initial state