Factory Method lets a class defer instantiation to subclasses.
example:
enum class TransportType {
TRUCK, SHIP
}
interface Transport {
fun deliver()
}
class Truck: Transport {
override fun deliver() {
println("Deliver by truck")
}
}
class Ship: Transport {
override fun deliver() {
println("Deliver by ship")
}
}
class LogisticFactory {
companion object {
fun createTransport(transportType: TransportType): Transport {
return when(transportType) {
TransportType.TRUCK -> Truck()
TransportType.SHIP -> Ship()
}
}
}
}
usage:
fun main() {
val transport = LogisticFactory.createTransport(TransportType.TRUCK)
transport.deliver()
}
Result:
Deliver by truck
Example:
enum class TransportType {
CAR, MOTORBIKE,
SHIP, BOAT
}
interface Transport {
fun deliver()
}
class Car: Transport {
override fun deliver() {
println("Deliver by car")
}
}
class MotorBike: Transport {
override fun deliver() {
println("Deliver by motorbike")
}
}
class Ship: Transport {
override fun deliver() {
println("Deliver by ship")
}
}
class Boat: Transport {
override fun deliver() {
println("Deliver by boat")
}
}
interface TransportFactory {
fun makeTransport(type: TransportType): Transport
}
class RoadTransportFactory: TransportFactory {
override fun makeTransport(type: TransportType): Transport {
return when(type) {
TransportType.CAR -> Car()
TransportType.MOTORBIKE -> MotorBike()
else -> throw IllegalArgumentException("Cannot find road transport type")
}
}
}
class SeaTransportFactory: TransportFactory {
override fun makeTransport(type: TransportType): Transport {
return when(type) {
TransportType.BOAT -> Boat()
TransportType.SHIP -> Ship()
else -> throw IllegalArgumentException("Cannot find sea transport type")
}
}
}
class LogisticFactory {
companion object {
fun createTransport(type: TransportType): Transport = when (type) {
TransportType.BOAT,
TransportType.SHIP -> {
val factory = SeaTransportFactory()
factory.makeTransport(type)
}
TransportType.CAR,
TransportType.MOTORBIKE -> {
val factory = RoadTransportFactory()
factory.makeTransport(type)
}
}
}
}
Usage:
val carDeliver = LogisticFactory.createTransport(TransportType.CAR)
carDeliver.deliver()
Result:
Deliver by car
Example:
class Dialog private constructor(
private val title: String?,
private val content: String?,
private val confirmText: String?,
private val cancelText: String?
) {
class Builder {
private var title: String? = null
private var content: String? = null
private var confirmText: String? = null
private var cancelText: String? = null
fun setTitle(title: String): Builder {
this.title = title
return this
}
fun setContent(content: String): Builder {
this.content = content
return this
}
fun setConfirmText(confirmText: String): Builder {
this.confirmText= confirmText
return this
}
fun setCancelText(cancelText: String): Builder {
this.cancelText = cancelText
return this
}
fun build(): Dialog {
return Dialog(title, content, confirmText, cancelText)
}
}
override fun toString(): String {
return "Dialog(title=$title, content=$content, confirmText=$confirmText, cancelText=$cancelText)"
}
}
Usage:
val dialog = Dialog.Builder()
.setTitle("Title")
.setContent("Hello")
.setConfirmText("OK")
.setCancelText("CANCEL")
.build()
println(dialog)
Result:
Dialog(title=Title, content=Hello, confirmText=OK, cancelText=CANCEL)
Example:
object Counter {
private var count = 0
fun count(): Int {
return ++count
}
}
Usage:
println(Counter.count())
println(Counter.count())
Result:
1
2
Example:
data class News(val title: String, val content: String)
Usage:
val firstNews = News("Breaking", "Broken")
val secondNews = firstNews.copy()
println(secondNews)
println("Are first news and second news the same? ${firstNews === secondNews}")
Result:
News(title=Breaking, content=Broken)
Are first news and second news the same? false
Example:
data class ListViewData(val title: String, val content: String)
class ListView {
fun showListViewData(list: List<ListViewData>) {
for (item in list) {
println(item)
}
}
}
/**
* Remote data is not compatible with LiveViewData
*/
data class RemoteData(val remoteTitle: String, val remoteContent: String)
class ListViewDataAdapter {
fun toListViewData(remoteData: RemoteData): ListViewData {
with(remoteData) {
return ListViewData(
remoteTitle,
remoteContent
)
}
}
}
Usage:
val listView = ListView()
val remoteList = listOf(
RemoteData("Breaking news", "broken news"),
RemoteData("Hello", "World")
)
// listView.showListViewData(remoteList) // error
val listViewDataAdapter = ListViewDataAdapter()
listView.showListViewData(remoteList.map { listViewDataAdapter.toListViewData(it) })
Result:
ListViewData(title=Breaking news, content=broken news)
ListViewData(title=Hello, content=World)
Example:
interface Device {
var enabled: Boolean
var volume: Int
var channel: Int
fun printStatus()
}
class Radio : Device {
override var enabled: Boolean = false
override var volume: Int = 30
set(value) {
field = when {
value > 100 -> 100
value < 0 -> 0
else -> value
}
}
override var channel: Int = 1
override fun printStatus() {
println("--------------------------")
println("| I'm radio.")
println("| I'm ${if (enabled) "enabled" else "disable"}")
println("| Current volume is $volume")
println("| Current channel is $channel")
println("--------------------------")
}
}
class Tv: Device {
override var enabled: Boolean = false
override var volume: Int = 30
set(value) {
field = when {
value > 100 -> 100
value < 0 -> 0
else -> value
}
}
override var channel: Int = 1
override fun printStatus() {
println("--------------------------")
println("| I'm TV.")
println("| I'm ${if (enabled) "enabled" else "disable"}")
println("| Current volume is $volume")
println("| Current channel is $channel")
println("--------------------------")
}
}
interface Remote {
fun power()
fun volumeDown()
fun volumeUp()
fun channelDown()
fun channelUp()
}
open class BasicRemote(
private val device: Device
): Remote {
override fun power() {
device.enabled = !device.enabled
}
override fun volumeDown() {
device.volume = device.volume - 10
}
override fun volumeUp() {
device.volume = device.volume + 10
}
override fun channelDown() {
device.channel = device.channel - 1
}
override fun channelUp() {
device.channel = device.channel + 1
}
}
class AdvancedRemote(
private val device: Device
): BasicRemote(device) {
fun mute() {
device.volume = 0
}
}
Usage:
val device: Device = Tv()
val advancedRemote = AdvancedRemote(device)
advancedRemote.mute()
advancedRemote.channelUp()
device.printStatus()
Result:
--------------------------
| I'm TV.
| I'm disable
| Current volume is 0
| Current channel is 2
--------------------------
Example:
interface View {
var x: Int
var y: Int
fun draw()
}
class LineView(override var x: Int, override var y: Int) : View {
override fun draw() {
println("Draw line to $x, $y")
}
}
class TextView(override var x: Int, override var y: Int,
val text: String): View {
override fun draw() {
println("Draw text $text to $x, $y")
}
}
class ViewGroup(
override var x: Int,
override var y: Int,
private val children: List<View>
): View {
override fun draw() {
println("Draw view group to $x, $y")
for (view in children) {
view.draw()
}
}
}
Usage:
val firstViewGroup = ViewGroup(1, 0,
listOf(LineView(10, 10), TextView(10, 20, "Hello"))
)
val secondViewGroup = ViewGroup(2, 0,
listOf(firstViewGroup, TextView(30, 30, "World"))
)
secondViewGroup.draw()
Result:
Draw view group to 2, 0
Draw view group to 1, 0
Draw line to 10, 10
Draw text Hello to 10, 20
Draw text World to 30, 30
Example:
interface DataSource {
fun writeData(data: String)
fun readData(): String
}
class ConsoleDataSource: DataSource {
private var data: String = ""
override fun writeData(data: String) {
this.data = data
println("Writing $data into console")
}
override fun readData(): String {
return data
}
}
open class DataSourceDecorator(
private val wrappee: DataSource
) : DataSource {
override fun writeData(data: String) {
wrappee.writeData(data)
}
override fun readData(): String {
return wrappee.readData()
}
}
class EncryptionDecorator(
private val dataSource: DataSource
): DataSourceDecorator(dataSource) {
override fun writeData(data: String) {
dataSource.writeData(encode(data))
}
override fun readData(): String {
return decode(dataSource.readData())
}
private fun encode(data: String): String {
return Base64.getEncoder().encodeToString(data.toByteArray())
}
private fun decode(data: String): String {
return String(Base64.getDecoder().decode(data))
}
}
Usage:
val plainDataSource = ConsoleDataSource()
plainDataSource.writeData("Important info")
val encryptedDataSource = EncryptionDecorator(plainDataSource)
encryptedDataSource.writeData("Important info")
println("Got decrypted data: ${encryptedDataSource.readData()}")
Result:
Writing Important info into console
Writing SW1wb3J0YW50IGluZm8= into console
Got decrypted data: Important info
Think about google search, what users see are an input view and a button. Google hides all complexity into their server.
Rather than create an object each time. we can cache the same object and share it by different clients.
Example:
okio library: ByteStrings and Buffers
Example:
interface ThirdPartyFileStore {
fun getFile(): String
}
class ThirdPartyFileStoreImpl: ThirdPartyFileStore {
override fun getFile(): String {
return Base64.getEncoder().encodeToString("confidential.txt".toByteArray())
}
}
class ProxyFileStore(
private val thirdPartyFileStore: ThirdPartyFileStore
): ThirdPartyFileStore {
override fun getFile(): String {
return thirdPartyFileStore.getFile()
}
fun getFileAndDecode(): String {
return String(Base64.getDecoder().decode(getFile()))
}
}
Usage:
val proxy = ProxyFileStore(ThirdPartyFileStoreImpl())
println("Got file ${proxy.getFile()}")
println("Got file ${proxy.getFileAndDecode()}")
Result:
Got file Y29uZmlkZW50aWFsLnR4dA==
Got file confidential.txt
Example:
fun sumWithCondition(integers: List<Int>, condition: (a: Int) -> Boolean): Int {
return integers.filter { condition(it) }.sum()
}
Usage:
println("Sum even numbers ${sumWithCondition(list) {it % 2 == 0}}")
println("Sum odd numbers ${sumWithCondition(list) {it % 2 != 0}}")
println("Sum all numbers ${sumWithCondition(list) { true }}")
Result:
Sum even numbers 20
Sum odd numbers 16
Sum all numbers 36
Example:
interface Observable <T> {
fun register(observer: Observer<T>)
fun unregister(observer: Observer<T>)
}
interface Observer<T> {
fun notify(t: T)
}
class WeatherReport: Observable<String> {
private val observers: HashSet<Observer<String>> = HashSet()
fun newReport(report: String) {
for (observer in observers) {
observer.notify(report)
}
}
override fun register(observer: Observer<String>) {
observers.add(observer)
}
override fun unregister(observer: Observer<String>) {
observers.remove(observer)
}
}
class TVStation: Observer<String> {
override fun notify(t: String) {
println("TV station got report: $t")
}
}
class EmailReceiver: Observer<String> {
override fun notify(t: String) {
println("Email receiver got report: $t")
}
}
Usage:
val weatherReport = WeatherReport()
val emailReceiver = EmailReceiver()
val tvStation = TVStation()
weatherReport.register(emailReceiver)
weatherReport.register(tvStation)
weatherReport.newReport("Cloudy, Temperature: 23 degrees")
weatherReport.unregister(emailReceiver)
weatherReport.unregister(tvStation)
Result:
TV station got report: Cloudy, Temperature: 23 degrees
Email receiver got report: Cloudy, Temperature: 23 degrees
Example:
abstract class View(private val children: List<View>) {
fun handleTouchEvent(event: TouchEvent): Boolean {
if (onEvent(event)) {
return true
}
for (child in children) {
if (child.handleTouchEvent(event)) {
return true
}
}
return false
}
abstract fun onEvent(event: TouchEvent): Boolean
}
class TextView(private val children: List<View>) : View(children) {
override fun onEvent(event: TouchEvent): Boolean {
println("I am TextView, I don't want to handle the event")
return false
}
}
class Button(private val children: List<View>): View(children) {
override fun onEvent(event: TouchEvent): Boolean {
println("I am Button, Let me handle the event")
return true
}
}
Usage:
val chain = TextView(listOf(TextView(listOf(Button(listOf(TextView(listOf())))))))
chain.handleTouchEvent(TouchEvent())
Result:
I am TextView, I don't want to handle the event
I am TextView, I don't want to handle the event
I am Button, Let me handle the event
Example:
interface Command {
fun execute()
}
class EditorService {
fun copy() {
println("Copy text")
}
fun cut() {
println("Cut text")
}
fun paste() {
println("Paste text")
}
}
class CopyCommand(private val editorService: EditorService): Command {
override fun execute() {
editorService.copy()
}
}
class PasteCommand(private val editorService: EditorService): Command {
override fun execute() {
editorService.paste()
}
}
class CutCommand(private val editorService: EditorService): Command {
override fun execute() {
editorService.cut()
}
}
typealias OnClickListener = () -> Unit
class EditorButton(
private val onClickListener: OnClickListener
) {
fun click() {
onClickListener.invoke()
}
}
class EditorGui(private val editorService: EditorService) {
val copyButton = EditorButton {
CopyCommand(editorService).execute()
}
val pasteButton = EditorButton {
PasteCommand(editorService).execute()
}
val cutButton = EditorButton {
CutCommand(editorService).execute()
}
}
Usage:
val editorGui = EditorGui(EditorService())
editorGui.copyButton.click()
editorGui.pasteButton.click()
editorGui.cutButton.click()
Result:
Copy text
Paste text
Cut text
Example:
interface Iterator <T> {
fun hasNext(): Boolean
fun next(): T
fun reset()
}
data class Friend(val name: String)
class MyFriends(
private val friends: Array<Friend>
): Iterator<Friend> {
private var position = 0
override fun hasNext(): Boolean {
return position < friends.size
}
override fun next(): Friend {
return friends[position++]
}
override fun reset() {
position = 0
}
}
Usage:
val myFriends = MyFriends(
arrayOf(Friend("Tony"), Friend("Tom"), Friend("TT"))
)
while (myFriends.hasNext()) {
println(myFriends.next())
}
Result:
Friend(name=Tony)
Friend(name=Tom)
Friend(name=TT)
Example:
sealed class NotifyType{
class CheckBox(val name: String, val isChecked: Boolean): NotifyType()
class RadioButton(val name: String, val selected: Int): NotifyType()
class Button(val name: String): NotifyType()
}
interface Mediator {
fun notify(notifyType: NotifyType)
}
class Dialog: Mediator {
val checkBox = CheckBox("myCheckBox", this)
val radioButton = RadioButton("myRadioButton", this)
val button = DialogButton("myButton", this)
override fun notify(notifyType: NotifyType) {
when(notifyType) {
is NotifyType.CheckBox -> {
println("${notifyType.name} is checked ${notifyType.isChecked}")
}
is NotifyType.RadioButton -> {
println("${notifyType.name} is selected ${notifyType.selected}")
}
is NotifyType.Button -> {
println("${notifyType.name} clicked")
}
}
}
}
open class Component(name: String, mediator: Mediator)
class DialogButton(
private val name: String, private val mediator: Mediator
): Component(name, mediator) {
fun onClick() {
mediator.notify(NotifyType.Button(name))
}
}
class RadioButton(
private val name: String, private val mediator: Mediator
): Component(name, mediator) {
fun select(num: Int) {
mediator.notify(NotifyType.RadioButton(name, num))
}
}
class CheckBox(
private val name: String, private val mediator: Mediator
): Component(name, mediator) {
fun onCheck(check: Boolean) {
mediator.notify(NotifyType.CheckBox(name, check))
}
}
Usage:
val dialog = Dialog()
dialog.checkBox.onCheck(true)
dialog.radioButton.select(2)
dialog.button.onClick()
Result:
myCheckBox is checked true
myRadioButton is selected 2
myButton clicked
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}")
originator.restore(careTaker.restore(1))
println("Second State: ${originator.state}")
originator.restore(careTaker.restore(0))
println("Third State: ${originator.state}")
originator.restore(careTaker.restore(1))
println("Last State: ${originator.state}")
Result:
Current State: State #3
Second State: State #2
Third State: initial state
Last State: State #2
Example:
sealed class UiState {
object Idle: UiState()
object Loading: UiState()
class Done(val data: String): UiState()
}
class UI {
private var state: UiState = UiState.Idle
fun showData() {
when (val thisState = state) {
is UiState.Done -> {
println("Show: ${thisState.data}")
}
is UiState.Loading -> {
println("Loading, please be patient")
}
is UiState.Idle -> {
println("Call fetch to update state")
}
}
}
fun fetch() {
state = UiState.Loading
}
fun done(remoteData: String) {
state = UiState.Done(remoteData)
}
}
Usage:
val ui = UI()
ui.showData()
ui.fetch()
ui.showData()
ui.done("Done")
ui.showData()
Result:
Call fetch to update state
Loading, please be patient
Show: Done
Example:
abstract class Downloader {
abstract val url: String
private var percentage = 0
fun startDownload() {
println("Start download $url")
while (percentage < 100) {
percentage += 10
onPercentage(percentage)
}
onDone()
}
protected abstract fun onPercentage(percentage: Int)
protected abstract fun onDone()
}
class VideoDownloader(override val url: String) : Downloader() {
override fun onDone() {
println("$url done")
}
override fun onPercentage(percentage: Int) {
println("Downloading..... $percentage%")
}
}
Usage:
val videoDownloader = VideoDownloader("https://video")
videoDownloader.startDownload()
Result:
Start download https://video
Downloading..... 10%
Downloading..... 20%
Downloading..... 30%
Downloading..... 40%
Downloading..... 50%
Downloading..... 60%
Downloading..... 70%
Downloading..... 80%
Downloading..... 90%
Downloading..... 100%
https://video done
Example:
interface Visitor {
fun visit(liquor: Liquor): Double
fun visit(tobacco: Tobacco): Double
fun visit(necessity: Necessity): Double
}
interface Visitable {
fun accept(visitor: Visitor): Double
}
abstract class Item {
abstract val price: Double
}
class Liquor(override val price: Double) : Item(), Visitable {
override fun accept(visitor: Visitor): Double {
return visitor.visit(this)
}
}
class Tobacco(override val price: Double) : Item(), Visitable {
override fun accept(visitor: Visitor): Double {
return visitor.visit(this)
}
}
class Necessity(override val price: Double) : Item(), Visitable {
override fun accept(visitor: Visitor): Double {
return visitor.visit(this)
}
}
class TaxVisitor: Visitor {
private val df = DecimalFormat("#.##")
override fun visit(liquor: Liquor): Double {
return df.format(liquor.price * .18 + liquor.price).toDouble()
}
override fun visit(tobacco: Tobacco): Double {
return df.format(tobacco.price * .32 + tobacco.price).toDouble()
}
override fun visit(necessity: Necessity): Double {
return df.format(necessity.price * .01 + necessity.price).toDouble()
}
}
Usage:
val taxVisitor = TaxVisitor()
val milk = Necessity(2.5)
println(milk.accept(taxVisitor))
val cigars = Tobacco(12.0)
println(cigars.accept(taxVisitor))
Result:
2.52
15.84