Saya memiliki aplikasi berdasarkan Squeryl. Saya mendefinisikan model saya sebagai kelas kasus, terutama karena saya merasa nyaman untuk memiliki metode salin.
Saya memiliki dua model yang sangat terkait. Field-field tersebut sama, banyak operasi yang sama, dan mereka akan disimpan dalam tabel DB yang sama. Tetapi ada beberapa perilaku yang hanya masuk akal dalam salah satu dari dua kasus, atau yang masuk akal dalam kedua kasus tetapi berbeda.
Sampai saat ini saya hanya menggunakan satu kelas kasus, dengan tanda yang membedakan jenis model, dan semua metode yang berbeda berdasarkan jenis model dimulai dengan jika. Ini menjengkelkan dan tidak cukup aman.
Yang ingin saya lakukan adalah memfaktorkan perilaku dan bidang umum di kelas kasus leluhur dan memiliki dua model aktual yang diwarisi darinya. Tapi, sejauh yang saya pahami, mewarisi dari kelas kasus tidak disukai di Scala, dan bahkan dilarang jika subkelas itu sendiri adalah kelas kasus (bukan kasus saya).
Apa masalah dan perangkap yang harus saya waspadai dalam mewarisi dari kelas kasus? Apakah masuk akal dalam kasus saya untuk melakukannya?
sumber
Jawaban:
Cara yang saya sukai untuk menghindari pewarisan kelas kasus tanpa duplikasi kode agak jelas: buat kelas dasar umum (abstrak):
abstract class Person { def name: String def age: Int // address and other properties // methods (ideally only accessors since it is a case class) } case class Employer(val name: String, val age: Int, val taxno: Int) extends Person case class Employee(val name: String, val age: Int, val salary: Int) extends Person
Jika Anda ingin lebih detail, kelompokkan properti menjadi ciri-ciri individu:
trait Identifiable { def name: String } trait Locatable { def address: String } // trait Ages { def age: Int } case class Employer(val name: String, val address: String, val taxno: Int) extends Identifiable with Locatable case class Employee(val name: String, val address: String, val salary: Int) extends Identifiable with Locatable
sumber
Karena ini adalah topik yang menarik bagi banyak orang, izinkan saya menjelaskan sedikit di sini.
Anda bisa menggunakan pendekatan berikut:
// You can mark it as 'sealed'. Explained later. sealed trait Person { def name: String } case class Employee( override val name: String, salary: Int ) extends Person case class Tourist( override val name: String, bored: Boolean ) extends Person
Ya, Anda harus menduplikasi bidang. Jika tidak, tidak mungkin menerapkan kesetaraan yang benar di antara masalah lainnya .
Namun, Anda tidak perlu menduplikasi metode / fungsi.
Jika duplikasi beberapa properti terlalu penting bagi Anda, gunakan kelas reguler, tetapi ingat bahwa mereka tidak cocok dengan FP.
Alternatifnya, Anda bisa menggunakan komposisi alih-alih pewarisan:
case class Employee( person: Person, salary: Int ) // In code: val employee = ... println(employee.person.name)
Komposisi adalah strategi yang valid dan bagus yang harus Anda pertimbangkan juga.
Dan jika Anda bertanya-tanya apa artinya sifat tersegel - itu adalah sesuatu yang hanya dapat diperpanjang dalam file yang sama. Artinya, kedua kelas kasus di atas harus berada dalam file yang sama. Ini memungkinkan untuk pemeriksaan kompiler yang lengkap:
val x = Employee(name = "Jack", salary = 50000) x match { case Employee(name) => println(s"I'm $name!") }
Memberikan kesalahan:
warning: match is not exhaustive! missing combination Tourist
Yang sangat berguna. Sekarang Anda tidak akan lupa untuk berurusan dengan tipe
Person
orang lain. Ini pada dasarnya adalah apa yang dilakukanOption
kelas di Scala.Jika itu tidak menjadi masalah bagi Anda, maka Anda dapat membuatnya tidak tersegel dan memasukkan kelas kasus ke dalam file mereka sendiri. Dan mungkin pergi dengan komposisi.
sumber
def name
sifat itu perluval name
. Kompiler saya memberi saya peringatan kode yang tidak dapat dijangkau dengan yang sebelumnya.kelas case sempurna untuk objek nilai, yaitu objek yang tidak mengubah properti apa pun dan dapat dibandingkan dengan persamaan.
Tetapi menerapkan yang setara dengan adanya warisan agak rumit. Pertimbangkan dua kelas:
class Point(x : Int, y : Int)
dan
class ColoredPoint( x : Int, y : Int, c : Color) extends Point
Jadi menurut definisi ColorPoint (1,4, merah) harus sama dengan Titik (1,4), mereka adalah Titik yang sama. Jadi ColorPoint (1,4, biru) juga harus sama dengan Titik (1,4), bukan? Tapi tentu saja ColorPoint (1,4, merah) tidak boleh sama dengan ColorPoint (1,4, biru), karena warnanya berbeda. Ini dia, satu properti dasar dari hubungan kesetaraan rusak.
memperbarui
Anda dapat menggunakan warisan dari sifat untuk memecahkan banyak masalah seperti yang dijelaskan dalam jawaban lain. Alternatif yang lebih fleksibel adalah sering menggunakan kelas tipe. Lihat Apa kegunaan kelas tipe di Scala? atau http://www.youtube.com/watch?v=sVMES4RZF-8
sumber
Employer.fire(e: Emplooyee)
tetapi tidak sebaliknya. Saya ingin membuat dua kelas yang berbeda, karena mereka sebenarnya mewakili objek yang berbeda, tetapi saya juga tidak menyukai pengulangan yang muncul.Dalam situasi ini saya cenderung menggunakan komposisi daripada pewarisan yaitu
sealed trait IVehicle // tagging trait case class Vehicle(color: String) extends IVehicle case class Car(vehicle: Vehicle, doors: Int) extends IVehicle val vehicle: IVehicle = ... vehicle match { case Car(Vehicle(color), doors) => println(s"$color car with $doors doors") case Vehicle(color) => println(s"$color vehicle") }
Jelas Anda dapat menggunakan hierarki dan kecocokan yang lebih canggih tetapi mudah-mudahan ini memberi Anda gambaran. Kuncinya adalah memanfaatkan ekstraktor bersarang yang disediakan kelas kasus
sumber