Design Patterns implemented in Kotlin

Overview

Design Patterns In Kotlin

⚠️ New article about testing: Unit Testing with Mockito 2

Project maintained by @dbacinski (Dariusz Baciński)

Inspired by Design-Patterns-In-Swift by @nsmeme (Oktawian Chojnacki)

Table of Contents

Behavioral

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.

Source: wikipedia.org

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

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

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

State

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.

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

Chain of Responsibility

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

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"
}

Visitor

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.

Example

interface ReportVisitable {
    fun <R> accept(visitor: ReportVisitor<R>): R
}

class FixedPriceContract(val costPerYear: Long) : ReportVisitable {
    override fun <R> accept(visitor: ReportVisitor<R>): R = visitor.visit(this)
}

class TimeAndMaterialsContract(val costPerHour: Long, val hours: Long) : ReportVisitable {
    override fun <R> accept(visitor: ReportVisitor<R>): R = visitor.visit(this)
}

class SupportContract(val costPerMonth: Long) : ReportVisitable {
    override fun <R> accept(visitor: ReportVisitor<R>): R = visitor.visit(this)
}

interface ReportVisitor<out R> {

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

class MonthlyCostReportVisitor : ReportVisitor<Long> {

    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<Long> {

    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

Mediator

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.

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<ChatUser> = 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!

Memento

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

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

    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

Creational

In software engineering, creational design patterns are design patterns that deal with object creation mechanisms, trying to create objects in a manner suitable to the situation. The basic form of object creation could result in design problems or added complexity to the design. Creational design patterns solve this problem by somehow controlling this object creation.

Source: wikipedia.org

Builder / Assembler

The builder pattern is used to create complex objects with constituent parts that must be created in the same order or using a specific algorithm. An external class controls the construction algorithm.

Example

// Let's assume that Dialog class is provided by external library.
// We have only access to Dialog public interface which cannot be changed.

class Dialog {

    fun showTitle() = println("showing title")

    fun setTitle(text: String) = println("setting title text $text")

    fun setTitleColor(color: String) = println("setting title color $color")

    fun showMessage() = println("showing message")

    fun setMessage(text: String) = println("setting message $text")

    fun setMessageColor(color: String) = println("setting message color $color")

    fun showImage(bitmapBytes: ByteArray) = println("showing image with size ${bitmapBytes.size}")

    fun show() = println("showing dialog $this")
}

//Builder:
class DialogBuilder() {
    constructor(init: DialogBuilder.() -> Unit) : this() {
        init()
    }

    private var titleHolder: TextView? = null
    private var messageHolder: TextView? = null
    private var imageHolder: File? = null

    fun title(init: TextView.() -> Unit) {
        titleHolder = TextView().apply { init() }
    }

    fun message(init: TextView.() -> Unit) {
        messageHolder = TextView().apply { init() }
    }

    fun image(init: () -> File) {
        imageHolder = init()
    }

    fun build(): Dialog {
        val dialog = Dialog()

        titleHolder?.apply {
            dialog.setTitle(text)
            dialog.setTitleColor(color)
            dialog.showTitle()
        }

        messageHolder?.apply {
            dialog.setMessage(text)
            dialog.setMessageColor(color)
            dialog.showMessage()
        }

        imageHolder?.apply {
            dialog.showImage(readBytes())
        }

        return dialog
    }

    class TextView {
        var text: String = ""
        var color: String = "#00000"
    }
}

Usage

//Function that creates dialog builder and builds Dialog
fun dialog(init: DialogBuilder.() -> Unit): Dialog {
    return DialogBuilder(init).build()
}

val dialog: Dialog = dialog {
	title {
    	text = "Dialog Title"
    }
    message {
        text = "Dialog Message"
        color = "#333333"
    }
    image {
        File.createTempFile("image", "jpg")
    }
}

dialog.show()

Output

setting title text Dialog Title
setting title color #00000
showing title
setting message Dialog Message
setting message color #333333
showing message
showing image with size 0
showing dialog Dialog@5f184fc6

Factory Method

The factory pattern is used to replace class constructors, abstracting the process of object generation so that the type of the object instantiated can be determined at run-time.

Example

sealed class Country {
    object USA : Country() //Kotlin 1.0 could only be an inner class or object
}

object Spain : Country() //Kotlin 1.1 declared as top level class/object in the same file
class Greece(val someProperty: String) : Country()
data class Canada(val someProperty: String) : Country() //Kotlin 1.1 data class extends other class
//object Poland : Country()

class Currency(
    val code: String
)

object CurrencyFactory {

    fun currencyForCountry(country: Country): Currency =
        when (country) {
            is Greece -> Currency("EUR")
            is Spain -> Currency("EUR")
            is Country.USA -> Currency("USD")
            is Canada -> Currency("CAD")
        }  //try to add a new country Poland, it won't even compile without adding new branch to 'when'
}

Usage

val greeceCurrency = CurrencyFactory.currencyForCountry(Greece("")).code
println("Greece currency: $greeceCurrency")

val usaCurrency = CurrencyFactory.currencyForCountry(Country.USA).code
println("USA currency: $usaCurrency")

assertThat(greeceCurrency).isEqualTo("EUR")
assertThat(usaCurrency).isEqualTo("USD")

Output

Greece currency: EUR
US currency: USD
UK currency: No Currency Code Available

Singleton

The singleton pattern ensures that only one object of a particular class is ever created. All further references to objects of the singleton class refer to the same underlying instance. There are very few applications, do not overuse this pattern!

Example:

object PrinterDriver {
    init {
        println("Initializing with object: $this")
    }

    fun print() = println("Printing with object: $this")
}

Usage

println("Start")
PrinterDriver.print()
PrinterDriver.print()

Output

Start
Initializing with object: PrinterDriver@6ff3c5b5
Printing with object: PrinterDriver@6ff3c5b5
Printing with object: PrinterDriver@6ff3c5b5

Abstract Factory

The abstract factory pattern is used to provide a client with a set of related or dependant objects. The "family" of objects created by the factory are determined at run-time.

Example

interface Plant

class OrangePlant : Plant

class ApplePlant : Plant

abstract class PlantFactory {
    abstract fun makePlant(): Plant

    companion object {
        inline fun <reified T : Plant> createFactory(): PlantFactory = when (T::class) {
            OrangePlant::class -> OrangeFactory()
            ApplePlant::class  -> AppleFactory()
            else               -> throw IllegalArgumentException()
        }
    }
}

class AppleFactory : PlantFactory() {
    override fun makePlant(): Plant = ApplePlant()
}

class OrangeFactory : PlantFactory() {
    override fun makePlant(): Plant = OrangePlant()
}

Usage

val plantFactory = PlantFactory.createFactory<OrangePlant>()
val plant = plantFactory.makePlant()
println("Created plant: $plant")

Output

Created plant: OrangePlant@4f023edb

Structural

In software engineering, structural design patterns are design patterns that ease the design by identifying a simple way to realize relationships between entities.

Source: wikipedia.org

Adapter

The adapter pattern is used to provide a link between two otherwise incompatible types by wrapping the "adaptee" with a class that supports the interface required by the client.

Example

interface Temperature {
    var temperature: Double
}

class CelsiusTemperature(override var temperature: Double) : Temperature

class FahrenheitTemperature(var celsiusTemperature: CelsiusTemperature) : Temperature {

    override var temperature: Double
        get() = convertCelsiusToFahrenheit(celsiusTemperature.temperature)
        set(temperatureInF) {
            celsiusTemperature.temperature = convertFahrenheitToCelsius(temperatureInF)
        }

    private fun convertFahrenheitToCelsius(f: Double): Double = (f - 32) * 5 / 9

    private fun convertCelsiusToFahrenheit(c: Double): Double = (c * 9 / 5) + 32
}

Usage

val celsiusTemperature = CelsiusTemperature(0.0)
val fahrenheitTemperature = FahrenheitTemperature(celsiusTemperature)

celsiusTemperature.temperature = 36.6
println("${celsiusTemperature.temperature} C -> ${fahrenheitTemperature.temperature} F")

fahrenheitTemperature.temperature = 100.0
println("${fahrenheitTemperature.temperature} F -> ${celsiusTemperature.temperature} C")

Output

36.6 C -> 97.88000000000001 F
100.0 F -> 37.77777777777778 C

Decorator

The decorator pattern is used to extend or alter the functionality of objects at run-time by wrapping them in an object of a decorator class. This provides a flexible alternative to using inheritance to modify behaviour.

Example

interface CoffeeMachine {
    fun makeSmallCoffee()
    fun makeLargeCoffee()
}

class NormalCoffeeMachine : CoffeeMachine {
    override fun makeSmallCoffee() = println("Normal: Making small coffee")

    override fun makeLargeCoffee() = println("Normal: Making large coffee")
}

//Decorator:
class EnhancedCoffeeMachine(val coffeeMachine: CoffeeMachine) : CoffeeMachine by coffeeMachine {

    // overriding behaviour
    override fun makeLargeCoffee() {
        println("Enhanced: Making large coffee")
        coffeeMachine.makeLargeCoffee()
    }

    // extended behaviour
    fun makeCoffeeWithMilk() {
        println("Enhanced: Making coffee with milk")
        coffeeMachine.makeSmallCoffee()
        println("Enhanced: Adding milk")
    }
}

Usage

    val normalMachine = NormalCoffeeMachine()
    val enhancedMachine = EnhancedCoffeeMachine(normalMachine)

    // non-overridden behaviour
    enhancedMachine.makeSmallCoffee()
    // overriding behaviour
    enhancedMachine.makeLargeCoffee()
    // extended behaviour
    enhancedMachine.makeCoffeeWithMilk()

Output

Normal: Making small coffee

Enhanced: Making large coffee
Normal: Making large coffee

Enhanced: Making coffee with milk
Normal: Making small coffee
Enhanced: Adding milk

Facade

The facade pattern is used to define a simplified interface to a more complex subsystem.

Example

class ComplexSystemStore(val filePath: String) {

    init {
        println("Reading data from file: $filePath")
    }

    val store = HashMap<String, String>()

    fun store(key: String, payload: String) {
        store.put(key, payload)
    }

    fun read(key: String): String = store[key] ?: ""

    fun commit() = println("Storing cached data: $store to file: $filePath")
}

data class User(val login: String)

//Facade:
class UserRepository {
    val systemPreferences = ComplexSystemStore("/data/default.prefs")

    fun save(user: User) {
        systemPreferences.store("USER_KEY", user.login)
        systemPreferences.commit()
    }

    fun findFirst(): User = User(systemPreferences.read("USER_KEY"))
}

Usage

val userRepository = UserRepository()
val user = User("dbacinski")
userRepository.save(user)
val resultUser = userRepository.findFirst()
println("Found stored user: $resultUser")

Ouput

Reading data from file: /data/default.prefs
Storing cached data: {USER_KEY=dbacinski} to file: /data/default.prefs
Found stored user: User(login=dbacinski)

Protection Proxy

The proxy pattern is used to provide a surrogate or placeholder object, which references an underlying object. Protection proxy is restricting access.

Example

interface File {
    fun read(name: String)
}

class NormalFile : File {
    override fun read(name: String) = println("Reading file: $name")
}

//Proxy:
class SecuredFile : File {
    val normalFile = NormalFile()
    var password: String = ""

    override fun read(name: String) {
        if (password == "secret") {
            println("Password is correct: $password")
            normalFile.read(name)
        } else {
            println("Incorrect password. Access denied!")
        }
    }
}

Usage

val securedFile = SecuredFile()
securedFile.read("readme.md")

securedFile.password = "secret"
securedFile.read("readme.md")

Ouput

Incorrect password. Access denied!
Password is correct: secret
Reading file: readme.md

Composite

The composite pattern is used to compose zero-or-more similar objects so that they can be manipulated as one object.

Example

open class Equipment(private var price: Int, private var name: String) {
    open fun getPrice(): Int = price
}


/*
[composite]
*/

open class Composite(name: String) : Equipment(0, name) {
    val equipments = ArrayList<Equipment>()

    fun add(equipment: Equipment) {
        this.equipments.add(equipment)
    }

    override fun getPrice(): Int {
        return equipments.map { it.getPrice() }.sum()
    }
}


/*
 leafs
*/

class Cabbinet : Composite("cabbinet")
class FloppyDisk : Equipment(70, "Floppy Disk")
class HardDrive : Equipment(250, "Hard Drive")
class Memory : Equipment(280, "Memory")

Usage

var cabbinet = Cabbinet()
cabbinet.add(FloppyDisk())
cabbinet.add(HardDrive())
cabbinet.add(Memory())
println(cabbinet.getPrice())

Ouput

600

Info

Descriptions from: Gang of Four Design Patterns Reference Sheet

Comments
  • Decorator doesn't demonstrate overriding behaviour, only extending it

    Decorator doesn't demonstrate overriding behaviour, only extending it

    As it is, the example compiles and has the same effect as if it didn't have : CoffeeMachine by coffeeMachine so it is really only demonstrating composition.

    //Decorator:
    class EnhancedCoffeeMachine(val coffeeMachine: CoffeeMachine) : CoffeeMachine by coffeeMachine {
    
        //overriding behaviour
        override fun makeLargeCoffee() = println("Enhanced: Making large coffee")
    
        //extending behaviour 
    
        fun makeCoffeeWithMilk() {
            println("Enhanced: Making coffee with milk")
            coffeeMachine.makeSmallCoffee()
            println("Enhanced: Adding milk")
        }
    
        fun makeDoubleLargeCoffee() {
            println("Enhanced: Making double large coffee")
            coffeeMachine.makeLargeCoffee()
            coffeeMachine.makeLargeCoffee()
        }
    }
    

    Usage:

        val normalMachine = NormalCoffeeMachine()
        val enhancedMachine = EnhancedCoffeeMachine(normalMachine)
    
        // non-overridden behaviour
        enhancedMachine.makeSmallCoffee()
        
        // overridden behaviour
        enhancedMachine.makeLargeCoffee()
        
        //extended behaviour (not sure why there's need for two examples of this)
        enhancedMachine.makeCoffeeWithMilk()
    
        enhancedMachine.makeDoubleLargeCoffee()
    
    opened by westonal 8
  • Use a list of observers instead of only one

    Use a list of observers instead of only one

    Usually, when implementing the observer pattern, the subject contains a list of observers, so it allows whatever class that is interested in its events to listen to them. In your (however beautiful) example there can be only one observer. So my suggestion is to use a list of observers in the subject, and a method named addObserver(observer: Any) to allow adding observers.

    opened by uzilan 5
  • Factory and object instead of class

    Factory and object instead of class

    Why should not we use an object instead of a class?

    object CurrencyFactory {
        fun currencyForCountry(country: Country): Currency? {
            return when (country) {
                Country.Spain, Country.Greece -> Euro()
                Country.UnitedStates -> UnitedStatesDollar()
                else -> null
            }
        }
    }
    
    opened by JavierSegoviaCordoba 3
  • Improvement For The Factory Example

    Improvement For The Factory Example

    The following code utilizes the sealed class keyword in Kotlin. I think it can be an improvement for the example in the Factory section. Will be glad to hear your opinion:

    interface Currency { val code: String }

    class Euro(override val code: String = "EUR") : Currency class UnitedStatesDollar(override val code: String = "USD") : Currency class UnitedKingdomPound(override val code: String = "GBP") : Currency

    sealed class Country

    data class UnitedStates(val name: String = "US"): Country() data class Spain(val name: String = "Spain"): Country() data class UK(val name: String = "UK"): Country() data class Greece(val name: String = "Greece"): Country()

    class CurrencyFactory { fun currencyForCountry(country: Country): Currency { return when (country) { is Spain, is Greece -> Euro() is UnitedStates -> UnitedStatesDollar() is UK -> UnitedKingdomPound() } } }

    opened by Amirko28 2
  • Changed Singleton to its natural static method getInstance()

    Changed Singleton to its natural static method getInstance()

    while your implementation works perfectly, it does not describe itself neither how it accepts the Singleton design pattern (https://en.wikipedia.org/wiki/Singleton_pattern)

    mostly, it's popular to grasp Singleton as a class with private constructor (therefore impossible be created outside itself) with a static function getInstance that can generate an "Instance" of this Singleton,

    that Instance is initiated as a classic member of itself as null and when getInstance is called, it will either call its private constructor to initiate itself or return the already initiated value

    Though this is called "Lazy implementation"...

    opened by Chaosruler972 2
  • How to mock UserRepository?

    How to mock UserRepository?

    I am planning to write an integration test. But how to mock UserRepository. https://github.com/dbacinski/Design-Patterns-In-Kotlin/blob/5b91b34e67fbb5e1938aa0408dd86ec3cc6ba716/patterns/src/test/kotlin/Facade.kt#L41

    opened by SAGARSURI 1
  • Builder and apply

    Builder and apply

    I think that is interesting how you create the builder pattern for learning Kotlin DSL, but why just don't use apply? Maybe add the apply alternative should be great because it is available for all objects out of the box.

    opened by JavierSegoviaCordoba 1
  • Stateless visitors

    Stateless visitors

    Made visitors stateless and provided a more generic implementation of the visitor pattern.

    I think it's much better to show a stateless implementation as an example for this pattern, which is more reusable, thread-safe and the reader can have a better understanding how to use it.

    opened by mgrzeszczak 1
  • README.md little change to conform to source (FactoryMethod) + suggestion

    README.md little change to conform to source (FactoryMethod) + suggestion

    while i was reading the README and playing with code, noticed a inconsistency in Factory Methods section, so i suggest a change. Also i took liberty in refactoring the when expression, its sounds more natural to me this way :).

    opened by mFrankowicz 1
  • Suggestions for State pattern.

    Suggestions for State pattern.

    Hello First, thanks for the project. I suggest few modifications:

    • removal of nested class imports
    • code simplifications
    • comment about the "empty class" Unauthorized
    opened by jolkdarr 0
  • Wrong usage of an Observer/Listener pattern example

    Wrong usage of an Observer/Listener pattern example

    I suppose that the usage example of Observer/Listener should be this:

    val textView = TextView().apply {
        listeners.add(PrintingTextChangedListener())
    }
    
    with(textView) {
        text = "Lorem ipsum"
        text = "dolor sit amet"
    }
    

    It is right in the pattern detail though.

    opened by hawklike 0
Owner
Dariusz Baciński
Loves trying out bleeding-edge technologies. Staunch believer in simple and readable code covered with a reasonable amount of tests.
Dariusz Baciński
Spring Kotlin Design Patterns

Spring Kotlin Design Patterns This is a project for learning design patterns in Kotlin Sources 1- GURU Requirements Java 11+ IntelliJ IDEA / Netbeans

Gustavo 2 Nov 22, 2022
An Android template project (in Kotlin) with boilerplate and current patterns.

android-starter-v4 An Android template project (in Kotlin) with boilerplate and plumbing, exploring current architecture patterns. A bit too much for

Matthias Urhahn 14 Nov 4, 2022
Note taking app using SOLID principles and CLEAN architecture patterns. Uses Room database, MVVM, Use cases

ShoppingNotes Note taking app using SOLID principles and CLEAN architecture patt

Chris Athanas 1 Dec 27, 2021
Blog implemented via the Storyblok Kotlin Multiplatform SDK (Android, JVM)

storyblok-mp-SDK-blog ... a showcase of using the Storyblok Kotlin Multiplatform Client to build a blog application (Android, JVM) What's included ??

Mike Penz 5 Sep 28, 2022
🎬 A demo project for The Movie DB based on Kotlin MVVM architecture and material design & animations.

TheMovies A simple project for The Movie DB based on Kotlin MVVM clean architecture and material design & animations. How to build on your environment

Jaewoong Eum 419 Jan 7, 2023
Simple Design for Kotlin bridge with Javascript. Also can get javascript console.log.

SDBridgeJava is here. If your h5 partner confused about how to deal with iOS and Android. This Demo maybe help. bilibili video introduction is here. Y

null 14 Dec 19, 2022
Same as an Outlined text fields presented in Material Design page but with some dynamic changes

README SSCustomEditTextOutlineBorder Getting Started SSCustomEditTextOutLineBorder is a small kotlin library for android to support outlined (stroked)

Simform Solutions 194 Dec 30, 2022
:octocat: A demo project based on MVVM architecture and material design & animations.

GithubFollows A simple demo project based on MVVM clean architecture and material design & animations. Architecture Specs & Open-source libraries Mini

Jaewoong Eum 289 Jan 4, 2023
NoteApp - Simple Note app using Room database and MVVM design pattern

General Used Room Database and Followed MVVM Architecture Pattern to build the a

Nasim Mahamud Shameem 1 Jun 29, 2022
It is far easier to design a class to be thread-safe than to retrofit it for thread safety later

"It is far easier to design a class to be thread-safe than to retrofit it for thread safety later." (Brian Goetz - Java concurrency: Publisher: Addiso

Nguyễn Trường Thịnh 3 Oct 26, 2022
CryptoMovies is a small app that show modern Android developement: with Hilt, Coroutines, Flow, Jetpack and Material Design 3

CryptoMovies is a small app that show modern Android developement: with Hilt, Coroutines, Flow, Jetpack and Material Design 3.

Leonardo Pirro 7 Sep 2, 2022
This app features - Modern Design, MVVM, Hilt, Room, StateFlow, Jetpack Compose

MyMusic is a beautiful app showing how to build modern looking interfaces using Jetpack Compose, recommended libraries, best practices and architectur

Ibrahim 54 Dec 27, 2022
Run Kotlin/JS libraries in Kotlin/JVM and Kotlin/Native programs

Zipline This library streamlines using Kotlin/JS libraries from Kotlin/JVM and Kotlin/Native programs. It makes it possible to do continuous deploymen

Cash App 1.5k Dec 30, 2022
A somewhat copy past of Jetbrain's code from the kotlin plugin repo to make it humanly possible to test Intellij IDEA kotlin plugins that work on kotlin

A somewhat copy past of Jetbrain's code from the kotlin plugin repo to make it humanly possible to test Intellij IDEA kotlin plugins that work on kotlin

common sense OSS 0 Jan 20, 2022
Real life Kotlin Multiplatform project with an iOS application developed in Swift with SwiftUI, an Android application developed in Kotlin with Jetpack Compose and a backed in Kotlin hosted on AppEngine.

Conferences4Hall Real life Kotlin Multiplatform project with an iOS application developed in Swift with SwiftUI, an Android application developed in K

Gérard Paligot 98 Dec 15, 2022
Android + Kotlin + Github Actions + ktlint + Detekt + Gradle Kotlin DSL + buildSrc = ❤️

kotlin-android-template ?? A simple Github template that lets you create an Android/Kotlin project and be up and running in a few seconds. This templa

Nicola Corti 1.5k Jan 3, 2023
LifecycleMvp 1.2 0.0 Kotlin is MVP architecture implementation with Android Architecture Components and Kotlin language features

MinSDK 14+ Download Gradle Add to project level build.gradle allprojects { repositories { ... maven { url 'https://jitpack.io' }

Robert 20 Nov 9, 2021
Opinionated Redux-like implementation backed by Kotlin Coroutines and Kotlin Multiplatform Mobile

CoRed CoRed is Redux-like implementation that maintains the benefits of Redux's core idea without the boilerplate. No more action types, action creato

Kittinun Vantasin 28 Dec 10, 2022
👋 A common toolkit (utils) ⚒️ built to help you further reduce Kotlin boilerplate code and improve development efficiency. Do you think 'kotlin-stdlib' or 'android-ktx' is not sweet enough? You need this! 🍭

Toolkit [ ?? Work in progress ⛏ ?? ??️ ?? ] Snapshot version: repositories { maven("https://s01.oss.sonatype.org/content/repositories/snapshots") }

凛 35 Jul 23, 2022