Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
package domain

import java.time.temporal.ChronoUnit
import java.time.{DayOfWeek, LocalDate, LocalDateTime}

import enumeratum._

sealed trait Age extends Ordered[Age] {
  def breachEncapsulationOfValue(today: LocalDate): Int

  override def compare(that: Age): Int = {
    val now = LocalDate.now()
    breachEncapsulationOfValue(now) compareTo that.breachEncapsulationOfValue(now)
  }
}

object Age {
  def fromBirthDay(birthDay: LocalDate): Age =
    DynamicAge(birthDay)

  def apply(value: Int): Age = StaticAge(value)
}

case class StaticAge(value: Int) extends Age {
  override def breachEncapsulationOfValue(today: LocalDate): Int = value
}

case class DynamicAge(birthDay: LocalDate) extends Age {

  private def calculateAge(birthDay: LocalDate, today: LocalDate): Int =
    ChronoUnit.YEARS.between(birthDay, today).toInt

  def breachEncapsulationOfValue(today: LocalDate): Int = calculateAge(birthDay, today)

}

sealed trait CustomerType extends EnumEntry

object CustomerType extends Enum[CustomerType] {
  override def values: IndexedSeq[CustomerType] = findValues

  case object General extends CustomerType

  case object UniversityStudent extends CustomerType // 大学生(専門含む)
  case object HighSchoolStudent extends CustomerType // 中高校生
  case object ElementarySchoolStudent extends CustomerType // 小学生
  case object Under5YearsOld extends CustomerType // 五歳以下
}

sealed trait Gender extends EnumEntry

object Gender extends Enum[Gender] {
  override def values: IndexedSeq[Gender] = findValues

  case object Male extends Gender

  case object Female extends Gender

}

case class Customer(birthDay: LocalDate, gender: Gender, customerType: CustomerType) { // Genderはまだ未使用

  def age(today: LocalDate): Age = Age.fromBirthDay(today)

}

sealed trait MoviePrice extends Ordered[MoviePrice] {
  def breachEncapsulationOfValue: Int

  override def compare(that: MoviePrice): Int ={
    breachEncapsulationOfValue compareTo that.breachEncapsulationOfValue
  }

}

class MoviePriceWeekday(customer: Customer, today: LocalDateTime) extends MoviePrice {
  import CustomerType._
  override def breachEncapsulationOfValue: Int = {
    (customer.customerType, customer.age(today.toLocalDate)) match {
      case (General, age) if age >= Age(70) => 1100
      case (General, _) => 1800
      case (UniversityStudent, _) => 1500
      case (HighSchoolStudent, _) => 1000
      case (ElementarySchoolStudent, _) => 1000
      case (Under5YearsOld, _) => 1000
    }
  }
}

class MoviePriceWeekdayLate(customer: Customer, today: LocalDateTime) extends MoviePrice {
  import CustomerType._
  override def breachEncapsulationOfValue: Int = {
    (customer.customerType, customer.age(today.toLocalDate)) match {
      case (General, age) if age >= Age(70) => 1100
      case (General, _) => 1300
      case (UniversityStudent, _) => 1300
      case (HighSchoolStudent, _) => 1000
      case (ElementarySchoolStudent, _) => 1000
      case (Under5YearsOld, _) => 1000
    }
  }
}

class MoviePriceWeekend(customer: Customer, today: LocalDateTime) extends MoviePrice {
  import CustomerType._
  override def breachEncapsulationOfValue: Int = {
    (customer.customerType, customer.age(today.toLocalDate)) match {
      case (General, age) if age >= Age(70) => 1100
      case (General, _) => 1800
      case (UniversityStudent, _) => 1500
      case (HighSchoolStudent, _) => 1000
      case (ElementarySchoolStudent, _) => 1000
      case (Under5YearsOld, _) => 1000
    }
  }
}

class MoviePriceWeekendLate(customer: Customer, today: LocalDateTime) extends MoviePrice {
  import CustomerType._

  def breachEncapsulationOfValue: Int = {
    (customer.customerType, customer.age(today.toLocalDate)) match {
      case (General, age) if age >= Age(70) => 1100
      case (General, _) => 1300
      case (UniversityStudent, _) => 1300
      case (HighSchoolStudent, _) => 1000
      case (ElementarySchoolStudent, _) => 1000
      case (Under5YearsOld, _) => 1000
    }
  }

}

// Domain Service
object MoviePriceCalculator {

  private def isWeekday(dateTime: LocalDateTime): Boolean =
    !(dateTime.getDayOfWeek == DayOfWeek.SATURDAY || dateTime.getDayOfWeek == DayOfWeek.SUNDAY)

  private def isLate(dateTime: LocalDateTime): Boolean = dateTime.getHour >= 20

  def moviePrice(customer: Customer, today: LocalDateTime): MoviePrice = {
    (isWeekday(today), isLate(today)) match {
      case (true, false) => new MoviePriceWeekday(customer, today)
      case (true, true) => new MoviePriceWeekdayLate(customer, today)
      case (false, false) => new MoviePriceWeekend(customer, today)
      case (false, true) => new MoviePriceWeekendLate(customer, today)
    }
  }

}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.