Thank you all for your answers so far.
Let’s put a concrete example to the table: We want to model a Person
class which can hold the following properties:
- Given name
- Surname(*)
- Date of birth
- Place of birth
- Nationality(*)
Surname and Nationality are mandatory, everything else is not.
Variant 1:
final case class Person(
surname: String,
nationality: String,
private val _givenName: String,
private val _dateOfBirth: LocalDateTime,
private val _placeOfBirth: String
) {
val givenName: Option[String] = Option(_givenName)
val dateOfBirth: Option[LocalDateTime] = Option(_dateOfBirth)
val placeOfBirth: Option[String] = Opion(_placeOfBirth)
}
object Person {
def apply(
surname: String,
nationality: String,
givenName: String = null,
dateOfBirth: LocalDateTime = null,
placeOfBirth: String = null
): Person = {
require(surname != null && nationality != null)
}
}
Variant 2:
final case class Person(
surname: String,
nationality: String,
givenName: Option[String] = None,
dateOfBirth: Option[LocalDateTime] = None,
placeOfBirth: Option[String] = None
) {
require(surname != null && nationality != null)
}
If only the mandatory fields are supplied, the call looks same for both variants:
val p = Person("Doe", "AT")
If we were to supply the mandatory values plus the place of birth things are different:
val p1 = Person("Doe", "AT", placeOfBirth = "New York City")
val p2 = Person("Doe", "AT", placeOfBirth = Some("New York City"))
In my opinion, the notation of variant 2 can become quite cumbersome, especially if there is a large number of parameters which are non-mandatory.