Skip to content

Commit

Permalink
m6l1 - Chain of Responsibility
Browse files Browse the repository at this point in the history
  • Loading branch information
svok authored and evgnep committed Sep 6, 2024
1 parent 15eb8cd commit 269b7cf
Show file tree
Hide file tree
Showing 8 changed files with 210 additions and 6 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package ru.otus.otuskotlin.marketplace.cor

@DslMarker
annotation class CorDslMarker
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package ru.otus.otuskotlin.marketplace.cor

import ru.otus.otuskotlin.marketplace.cor.handlers.CorChainDsl
import ru.otus.otuskotlin.marketplace.cor.handlers.CorWorkerDsl

/**
* Базовый билдер (dsl)
*/
@CorDslMarker
interface ICorExecDsl<T> {
var title: String
var description: String
fun on(function: suspend T.() -> Boolean)
fun except(function: suspend T.(e: Throwable) -> Unit)

fun build(): ICorExec<T>
}

/**
* Билдер (dsl) для цепочек (chain)
*/
@CorDslMarker
interface ICorChainDsl<T> : ICorExecDsl<T> {
fun add(worker: ICorExecDsl<T>)
}

/**
* Билдер (dsl) для рабочих (worker)
*/
@CorDslMarker
interface ICorWorkerDsl<T> : ICorExecDsl<T> {
fun handle(function: suspend T.() -> Unit)
}

/**
* Точка входа в dsl построения цепочек.
* Элементы исполняются последовательно.
*
* Пример:
* ```
* rootChain<SomeContext> {
* worker {
* }
* chain {
* worker(...) {
* }
* worker(...) {
* }
* }
* }
* ```
*/
fun <T> rootChain(function: ICorChainDsl<T>.() -> Unit): ICorChainDsl<T> = CorChainDsl<T>().apply(function)


/**
* Создает цепочку, элементы которой исполняются последовательно.
*/
fun <T> ICorChainDsl<T>.chain(function: ICorChainDsl<T>.() -> Unit) {
add(CorChainDsl<T>().apply(function))
}

/**
* Создает рабочего
*/
fun <T> ICorChainDsl<T>.worker(function: ICorWorkerDsl<T>.() -> Unit) {
add(CorWorkerDsl<T>().apply(function))
}

/**
* Создает рабочего с on и except по умолчанию
*/
fun <T> ICorChainDsl<T>.worker(
title: String,
description: String = "",
blockHandle: T.() -> Unit
) {
add(CorWorkerDsl<T>().also {
it.title = title
it.description = description
it.handle(blockHandle)
})
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package ru.otus.otuskotlin.marketplace.cor.handlers

import ru.otus.otuskotlin.marketplace.cor.ICorExec
import ru.otus.otuskotlin.marketplace.cor.ICorExecDsl

abstract class AbstractCorExec<T>(
override val title: String,
override val description: String = "",
private val blockOn: suspend T.() -> Boolean = { true },
private val blockExcept: suspend T.(Throwable) -> Unit = {},
): ICorExec<T> {
protected abstract suspend fun handle(context: T)

private suspend fun on(context: T): Boolean = context.blockOn()
private suspend fun except(context: T, e: Throwable) = context.blockExcept(e)

override suspend fun exec(context: T) {
if (on(context)) {
try {
handle(context)
} catch (e: Throwable) {
except(context, e)
}
}
}
}

abstract class CorExecDsl<T> : ICorExecDsl<T> {
protected var blockOn: suspend T.() -> Boolean = { true }
protected var blockExcept: suspend T.(e: Throwable) -> Unit = { e: Throwable -> throw e }

override var title: String = ""
override var description: String = ""

override fun on(function: suspend T.() -> Boolean) {
blockOn = function
}

override fun except(function: suspend T.(e: Throwable) -> Unit) {
blockExcept = function
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package ru.otus.otuskotlin.marketplace.cor.handlers

import ru.otus.otuskotlin.marketplace.cor.CorDslMarker
import ru.otus.otuskotlin.marketplace.cor.ICorChainDsl
import ru.otus.otuskotlin.marketplace.cor.ICorExec
import ru.otus.otuskotlin.marketplace.cor.ICorExecDsl

/**
* Реализация цепочки (chain), которая исполняет свои вложенные цепочки и рабочие
*/
class CorChain<T>(
private val execs: List<ICorExec<T>>,
title: String,
description: String = "",
blockOn: suspend T.() -> Boolean = { true },
blockExcept: suspend T.(Throwable) -> Unit = {},
) : AbstractCorExec<T>(title, description, blockOn, blockExcept) {
override suspend fun handle(context: T) {
execs.forEach {
it.exec(context)
}
}
}

@CorDslMarker
class CorChainDsl<T>(
) : CorExecDsl<T>(), ICorChainDsl<T> {
private val workers: MutableList<ICorExecDsl<T>> = mutableListOf()
override fun add(worker: ICorExecDsl<T>) {
workers.add(worker)
}

override fun build(): ICorExec<T> = CorChain(
title = title,
description = description,
execs = workers.map { it.build() },
blockOn = blockOn,
blockExcept = blockExcept
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package ru.otus.otuskotlin.marketplace.cor.handlers

import ru.otus.otuskotlin.marketplace.cor.CorDslMarker
import ru.otus.otuskotlin.marketplace.cor.ICorExec
import ru.otus.otuskotlin.marketplace.cor.ICorWorkerDsl

class CorWorker<T>(
title: String,
description: String = "",
blockOn: suspend T.() -> Boolean = { true },
private val blockHandle: suspend T.() -> Unit = {},
blockExcept: suspend T.(Throwable) -> Unit = {},
) : AbstractCorExec<T>(title, description, blockOn, blockExcept) {
override suspend fun handle(context: T) = blockHandle(context)
}

@CorDslMarker
class CorWorkerDsl<T> : CorExecDsl<T>(), ICorWorkerDsl<T> {
private var blockHandle: suspend T.() -> Unit = {}
override fun handle(function: suspend T.() -> Unit) {
blockHandle = function
}

override fun build(): ICorExec<T> = CorWorker(
title = title,
description = description,
blockOn = blockOn,
blockHandle = blockHandle,
blockExcept = blockExcept
)

}
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package ru.otus.otuskotlin.marketplace.cor

import kotlinx.coroutines.test.runTest
import ru.otus.otuskotlin.marketplace.cor.handlers.CorChain
import ru.otus.otuskotlin.marketplace.cor.handlers.CorWorker
import kotlin.test.Test
import kotlin.test.assertEquals

class CorChainTest {
/*@Test
@Test
fun `chain should execute workers`() = runTest {
val createWorker = { title: String ->
CorWorker<TestContext>(
Expand All @@ -21,5 +23,5 @@ class CorChainTest {
val ctx = TestContext()
chain.exec(ctx)
assertEquals("w1; w2; ", ctx.history)
}*/
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import kotlin.test.assertEquals
import kotlin.test.assertFails

class CorBaseTest {
/*private suspend fun execute(dsl: ICorExecDsl<TestContext>): TestContext {
private suspend fun execute(dsl: ICorExecDsl<TestContext>): TestContext {
val ctx = TestContext()
dsl.build().exec(ctx)
return ctx
Expand Down Expand Up @@ -100,6 +100,6 @@ class CorBaseTest {

private fun ICorChainDsl<TestContext>.printResult() = worker(title = "Print example") {
println("some = $some")
}*/
}
}

Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
package ru.otus.otuskotlin.marketplace.cor

import kotlinx.coroutines.test.runTest
import ru.otus.otuskotlin.marketplace.cor.handlers.CorWorker
import kotlin.test.Test
import kotlin.test.assertEquals

class CorWorkerTest {
/*@Test
@Test
fun `worker should execute handle`() = runTest {
val worker = CorWorker<TestContext>(
title = "w1",
Expand Down Expand Up @@ -39,5 +40,5 @@ class CorWorkerTest {
worker.exec(ctx)
assertEquals("some error", ctx.history)
}
*/

}

0 comments on commit 269b7cf

Please sign in to comment.