как новичок в Scala - функциональном способе, я немного путаюсь, следует ли мне помещать функции/методы для моего класса case внутри такого класса (а затем использовать такие вещи, как цепочка методов, подсказка IDE) или более функциональный подход для определения функций вне класса case. Рассмотрим оба подхода к очень простой реализации кольцевого буфера:
1/методы внутри класса case
case class RingBuffer[T](index: Int, data: Seq[T]) {
def shiftLeft: RingBuffer[T] = RingBuffer((index + 1) % data.size, data)
def shiftRight: RingBuffer[T] = RingBuffer((index + data.size - 1) % data.size, data)
def update(value: T) = RingBuffer(index, data.updated(index, value))
def head: T = data(index)
def length: Int = data.length
}
Используя этот подход, вы можете делать такие вещи, как методы цепочки, и среда IDE сможет в этом случае намекать на методы:
val buffer = RingBuffer(0, Seq(1,2,3,4,5)) // 1,2,3,4,5
buffer.head // 1
val buffer2 = buffer.shiftLeft.shiftLeft // 3,4,5,1,2
buffer2.head // 3
2/функции вне класса case
case class RingBuffer[T](index: Int, data: Seq[T])
def shiftLeft[T](rb: RingBuffer[T]): RingBuffer[T] = RingBuffer((rb.index + 1) % rb.data.size, rb.data)
def shiftRight[T](rb: RingBuffer[T]): RingBuffer[T] = RingBuffer((rb.index + rb.data.size - 1) % rb.data.size, rb.data)
def update[T](value: T)(rb: RingBuffer[T]) = RingBuffer(rb.index, rb.data.updated(rb.index, value))
def head[T](rb: RingBuffer[T]): T = rb.data(rb.index)
def length[T](rb: RingBuffer[T]): Int = rb.data.length
Этот подход кажется более функциональным для меня, но я не уверен, насколько он практичен, потому что, например, IDE не сможет намекать на все возможные вызовы методов, как использование методов в предыдущем примере.
val buffer = RingBuffer(0, Seq(1,2,3,4,5)) // 1,2,3,4,5
head(buffer) // 1
val buffer2 = shiftLeft(shiftLeft(buffer)) // 3,4,5,1,2
head(buffer2) // 3
Используя этот подход, функциональность оператора трубы может сделать вышеприведенную третью строку более читаемой:
implicit class Piped[A](private val a: A) extends AnyVal {
def |>[B](f: A => B) = f( a )
}
val buffer2 = buffer |> shiftLeft |> shiftLeft
Не могли бы вы рассказать мне свое собственное мнение об авансе/неудобстве конкретного подхода и о том, какое общее правило, когда использовать какой подход (если есть)?
Большое спасибо.