Search code examples
scalaobject-oriented-analysis

Put method in trait or in case class?


There are two ways of defining a method for two different classes inheriting the same trait in Scala.

sealed trait Z { def minus: String }
case class A() extends Z { def minus = "a" }
case class B() extends Z { def minus = "b" }

The alternative is the following:

sealed trait Z { def minus: String = this match {
    case A() => "a"
    case B() => "b"
}
case class A() extends Z
case class B() extends Z

The first method repeats the method name, whereas the second method repeats the class name.

I think that the first method is the best to use because the codes are separated. However, I found myself often using the second one for complicated methods, so that adding additional arguments can be done very easily for example like this:

sealed trait Z {
  def minus(word: Boolean = false): String = this match {
    case A() => if(word) "ant" else "a"
    case B() => if(word) "boat" else "b"
}
case class A() extends Z
case class B() extends Z

What are other differences between those practices? Are there any bugs that are waiting for me if I choose the second approach?

EDIT: I was quoted the open/closed principle, but sometimes, I need to modify not only the output of the functions depending on new case classes, but also the input because of code refactoring. Is there a better pattern than the first one? If I want to add the previous mentioned functionality in the first example, this would yield the ugly code where the input is repeated:

sealed trait Z { def minus(word: Boolean): String  ; def minus = minus(false) }
case class A() extends Z { def minus(word: Boolean) = if(word) "ant" else "a" }
case class B() extends Z { def minus(word: Boolean) = if(word) "boat" else "b" }

Solution

  • Starting in Scala 3, you have the possibility to use trait parameters (just like classes have parameters), which simplifies things quite a lot in this case:

    trait Z(x: String) { def minus: String = x }
    case class A() extends Z("a")
    case class B() extends Z("b")
    A().minus // "a"
    B().minus // "b"