Frage

Ich verwende Fallklassen für Modelle in einem ORM.Jedes Modell hat eine ID, aber die ID sollte nicht öffentlich zugänglich sein.Ich habe also eine Elterneigenschaft

trait WithId {
  private var id: Long = 0
}

und viele Fallklassen (die Modelle), die davon erben

case class C1(a: Int, b: String) extends WithId
case class C2(...) extends WithId
...

Wenn nun jemand copy() für eine Fallklasse aufruft, wird die ID nicht mitkopiert, sondern auf 0 gesetzt.

val c1 = C1(3, "bla")
//Set c1.id to a value != 0
val c2 = c1.copy(b="bla2")
//c1.id !=0, but c2.id = 0

Ich möchte, dass auch die ID kopiert wird.

Da ich viele dieser Fallklassen habe, würde ich es vorziehen, so wenig Code wie möglich in den Fallklassen selbst zu haben.Die Implementierung einer copy()-Methode in jeder Case-Klasse würde also eine Menge Boilerplate-Code erfordern.

Gibt es eine Möglichkeit, etwas in das Merkmal zu implementieren, das dazu führt, dass copy() auch die ID kopiert?Vielleicht etwas mit Makros?Oder gibt es eine andere Möglichkeit, an die ich überhaupt nicht gedacht habe?

bearbeiten:

Ich könnte das ID-Feld in jeder Fallklasse überschreiben

case class C1(a: Int, b: String, protected var id: Long)

Dann würde es kopiert werden.Aber das ist auch Boilerplate-Code, den ich pro Fallklasse schreiben muss, und es ist schwer zu erklären, warum man einer Fallklasse ein ID-Feld hinzufügen muss, obwohl man es nie bemerkt oder es bei Verwendung der Fallklasse irgendwo anders verwenden kann.Das möchte ich nach Möglichkeit vermeiden.

War es hilfreich?

Lösung

Wenn ich Sie wäre, würde ich einen ID-tragenden Token hinzufügen:

class IdToken private[myPackage] (val id: Int) {
  override def equals(a: Any) = a match {
    case tok: IdToken => id == tok.id
    case _ => false
  }
  override def hashCode = id.hashCode ^ 0x157135
  override def toString = ""
}
object IdToken {
  private var lastId = 0
  private[myPackage] def next = { lastId += 1; new IdToken(lastId) }
}

Da der Konstruktor für Ihr Paket privat ist, kann niemand außer Ihnen diese Token erstellen.

Dann schreiben Sie Ihr Merkmal als

trait WithID { protected def idToken: IdToken }

Und

case class C1(a: Int, b: String, protected val idToken: IdToken = IdToken.next) extends WithID {
  def checkId = idToken.id
}

(wo Sie nur brauchen checkId für den Test unten), damit Sie es können

scala> C1(5, "fish")
res1: C1 = C1(5,fish,)

scala> res1.copy(a = 3)
res2: C1 = C1(3,fish,)

scala> res1.checkId == res2.checkId
res3: Boolean = true

Sie können jedoch nicht von außerhalb des Codes auf das Token zugreifen, da der val Ist protected.

Hoffentlich ist die Kapselung gut genug.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top