Skip to content

Instantly share code, notes, and snippets.

@nicolaquartieri
Created April 3, 2021 13:58
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nicolaquartieri/b2aff39fa68dae8fbc9ad64f2072e620 to your computer and use it in GitHub Desktop.
Save nicolaquartieri/b2aff39fa68dae8fbc9ad64f2072e620 to your computer and use it in GitHub Desktop.
Hidden Temporal Couplings.
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Test
import post.domain.MENU.*
/**
* Hidden Temporal Couplings with a simple TDD example.
*
* In this example Luigi [ItalianCook] and Louis [FrenchCook], the Italian and the French cook,
* are requested to make two kind of pizzas but each of them use a different approach.
*/
class PizzaTest {
@Test
fun `should the Italian cook provide one pepperoni pizza`() {
val luigi = ItalianCook()
val pizza = luigi.bakeOne(PEPPERONI_PIZZA)
assertTrue(pizza.isCooked())
}
@Test
fun `should the Italian cook provide one pepperoni pizza without sauce`() {
val luigi = ItalianCook()
val pizza: Dish = luigi.bakeOne(BIANCA_PIZZA)
assertTrue(pizza.isCooked())
assertFalse(pizza.hasSauce())
}
@Test
fun `should the French cook provide one pepperoni pizza`() {
val louis = FrenchCook()
val pizza = louis.bakeOne(PEPPERONI_PIZZA)
assertTrue(pizza.isCooked())
}
@Test
fun `should the French cook provide one pepperoni pizza without sauce`() {
val mario = FrenchCook()
val pizza: Dish = mario.bakeOne(BIANCA_PIZZA)
assertTrue(pizza.isCooked())
assertFalse(pizza.hasSauce())
}
}
enum class MENU {
PEPPERONI_PIZZA, BIANCA_PIZZA
}
class Ingredients(val menu: MENU? = null) {
var arePrepared = false
init { arePrepared = menu != null }
}
class Sauce(ingredients: Ingredients) {
var isPrepared = false
init {
isPrepared = when (ingredients.menu) {
PEPPERONI_PIZZA -> true
else -> false
}
}
}
class Dough(ingredients: Ingredients) {
var isPrepared = false
init { isPrepared = ingredients.arePrepared }
private var sauce: Sauce = Sauce(Ingredients())
fun topWith(sauce: Sauce) {
this.sauce = sauce
}
fun hasSauce() = sauce.isPrepared
}
class ItalianCook: Cook {
override fun bakeOne(menu: MENU): Dish {
// This way it is possible to expose the temporal coupling.
val ingredients = prepareIngredients(menu)
val dough = prepareDough(ingredients)
val sauce = prepareSauce(ingredients)
topDough(dough, sauce)
return bake(dough)
}
private fun topDough(dough: Dough, sauce: Sauce) {
if (sauce.isPrepared) dough.topWith(sauce)
}
class Pizza(val dough: Dough) : Dish {
private var state = false
init {
state = true // We trust evey thing went well :)
}
override fun isCooked() = state
override fun hasSauce() = dough.hasSauce()
}
private fun bake(dough: Dough): Dish {
return Pizza(dough)
}
private fun prepareSauce(ingredients: Ingredients): Sauce {
return Sauce(ingredients)
}
// Combine flour, salt and liquids.
private fun prepareDough(ingredients: Ingredients) = Dough(ingredients)
// Prepare the "Mise en place".
private fun prepareIngredients(selectedMenu: MENU) = Ingredients(selectedMenu)
}
class FrenchCook: Cook {
lateinit var selectedMenu: MENU
lateinit var ingredients: Ingredients
lateinit var dough: Dough
lateinit var sauce: Sauce
override fun bakeOne(menu: MENU): Dish {
this.selectedMenu = menu
// This way the temporal coupling is not enforce.
prepareIngredients()
prepareDough()
prepareSauce()
topDough()
return bake()
}
private fun topDough() {
if (sauce.isPrepared) dough.topWith(sauce)
}
class Pizza(val dough: Dough) : Dish {
private var state = false
init {
state = true // We trust evey thing went well :)
}
override fun isCooked() = state
override fun hasSauce() = dough.hasSauce()
}
private fun bake(): Dish {
return Pizza(dough)
}
private fun prepareSauce() {
sauce = Sauce(ingredients)
}
private fun prepareDough() {
// Combine flour, salt and liquids.
dough = Dough(ingredients)
}
private fun prepareIngredients() {
// Prepare the "Mise en place".
ingredients = Ingredients(selectedMenu)
}
}
interface Dish {
fun isCooked(): Boolean
fun hasSauce(): Boolean
}
interface Cook {
fun bakeOne(menu: MENU): Dish
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment