Created
April 3, 2021 13:58
-
-
Save nicolaquartieri/b2aff39fa68dae8fbc9ad64f2072e620 to your computer and use it in GitHub Desktop.
Hidden Temporal Couplings.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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