Skip to content

Instantly share code, notes, and snippets.

@nobuoka
Last active February 16, 2019 05:31
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 nobuoka/8019bbbe3b5cf54b8d4873176b947768 to your computer and use it in GitHub Desktop.
Save nobuoka/8019bbbe3b5cf54b8d4873176b947768 to your computer and use it in GitHub Desktop.
// === ドメインレイヤ ===
sealed class FizzBuzzValue {
abstract val expression: String
data class Number(override val expression: String) : FizzBuzzValue()
object Fizz : FizzBuzzValue() { override val expression: String = "Fizz" }
object Buzz : FizzBuzzValue() { override val expression: String = "Buzz" }
object FizzBuzz : FizzBuzzValue() { override val expression: String = "FizzBuzz" }
}
object FizzBuzzValueFactory {
fun create(inputNumber: Int): FizzBuzzValue = run {
val hasFizz = inputNumber % 3 == 0
val hasBuzz = inputNumber % 5 == 0
return when {
hasFizz && hasBuzz -> FizzBuzzValue.FizzBuzz
hasFizz -> FizzBuzzValue.Fizz
hasBuzz -> FizzBuzzValue.Buzz
else -> FizzBuzzValue.Number(inputNumber.toString())
}
}
}
// === アプリケーションレイヤ ===
/**
* 指定の出力先に Fizz Buzz を表示するアプリケーションサービス。
*
* @param output 表示先に出力する処理。 Fizz Buzz の値ごとに呼ばれる。
*/
class ShowFizzBuzzService(private val output: (String) -> Unit) {
/**
* 指定された範囲の数値に対応する Fizz Buzz を表示する。
*
* @param inputNumbers 対象の数値の範囲。
*/
operator fun invoke(inputNumbers: IntRange) {
inputNumbers.map(FizzBuzzValueFactory::create).map(FizzBuzzValue::expression).forEach(output)
}
}
// === main 関数 ===
fun main() {
val showFizzBuzz = ShowFizzBuzzService(::println)
showFizzBuzz(1..15)
}
// === ドメインレイヤ ===
class FizzBuzzConfigItem(
/** この条件が真になる場合に [expression] が出力に含まれる。 */
val predicate: (Int) -> Boolean,
/** 条件が真の場合に出力される文字列。 */
val expression: String
)
class FizzBuzzCalculator(private val config: List<FizzBuzzConfigItem>) {
fun calculate(inputNumber: Int): String = run {
val expressions = config.filter { it.predicate(inputNumber) }.map(FizzBuzzConfigItem::expression)
if (expressions.isEmpty()) {
inputNumber.toString()
} else {
expressions.joinToString("")
}
}
companion object {
/**
* 3 で割り切れる場合は 「Fizz」、5 で割り切れる場合は 「Buzz」 になるインスタンス。
*/
val normalInstance by lazy {
FizzBuzzCalculator(listOf(
FizzBuzzConfigItem({ it % 3 == 0 }, "Fizz"),
FizzBuzzConfigItem({ it % 5 == 0 }, "Buzz")
))
}
}
}
// === アプリケーションレイヤ ===
/**
* 指定の出力先に Fizz Buzz を表示するアプリケーションサービス。
*
* @param output 表示先に出力する処理。 Fizz Buzz の値ごとに呼ばれる。
*/
class ShowNormalFizzBuzzService(private val output: (String) -> Unit) {
/**
* 指定された範囲の数値に対応する Fizz Buzz を表示する。
*
* @param inputNumbers 対象の数値の範囲。
*/
operator fun invoke(inputNumbers: IntRange) {
val calculator = FizzBuzzCalculator.normalInstance
inputNumbers.map(calculator::calculate).forEach(output)
}
}
// === main 関数 ===
fun main() {
val showFizzBuzz = ShowNormalFizzBuzzService(::println)
showFizzBuzz(1..15)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment