I am learning about Scala case classes, and design patterns. To this end I have created an example below which I think is a fairly likely scenario when working with Json type data. I know libraries exist that do this stuff, but I am doing it manually to learn Scala approaches to solving problems, as using a library won't help me learn.
The main design improvement I want to do is abstracting common code.
Suppose my codebase consists of many case classes, where each case class is serializable:
trait Base {
def serialize(): String
}
trait Animal extends Base
trait Mammal extends Animal
trait Reptile extends Animal
case class Lizard(name: String, tail: Boolean) extends Reptile {
def serialize(): String = s"""{name: $name, tail: $tail}"""
}
case class Cat(name: String, age: Int) extends Mammal {
def serialize(): String = s"""{name: $name, age: $age}"""
}
case class Fish(species: String) extends Animal {
def serialize(): String = s"""{species: $species}"""
}
case class Pets(group_name: String, cat: Option[Cat] = None, lizard: Option[Lizard] = None, fish: Fish) extends Base {
def serialize(): String = {
// cat and lizard serialize in a similar fashion
val cat_object = cat match {
case Some(c) => s"""cats: ${c.serialize()}"""
case _ => ""
}
val lizard_object = lizard match {
case Some(d) => s"""lizards: ${d.serialize()}"""
case _ => ""
}
// fish serializes in a different way as it is not an option
val fish_object = s"""fish: ${fish.serialize()}"""
s"""{$lizard_object, $cat_object, $fish_object}"""
}
}
val bob = Cat("Bob", 42)
val jill = Lizard("Jill", true)
val pets = Pets("my group", Some(bob), Some(jill), Fish("goldfish")).serialize()
println(pets)
}
Now there is a repetitive pattern here:
In Pets, when I am serializing, I am basically going over each (key, value) pair (except group_name) in the parameter list and doing the following:
key: value.serialize()
Now I do not know the form of value, it can be an option as in the example. Furthermore, suppose I have many classes like Pets. In that case I would have to manually write many pattern matches on every argument where required, distinguishing between String, Int, Option[String], etc. Would there be a way to abstract out this serializable operation so that if I have many case classes like Pets, I can simply run a single function and get the correct result.
I asked a related question here about getting declared field from cases classes, but it seems that way is not type safe and may create issues later down the line if I add more custom case classes:
https://stackoverflow.com/questions/62662417/how-to-get-case-class-parameter-key-value-pairs