I have two kinds of reviews for a product:
- Works as suggested (1 to 5).
- Price okay. (1 to 5).
I'd like WorksFineRating instances to be stored in WorksFine table in SQL. I'd like PriceOkayRating instances to be stored in PriceOkay table in SQL.
I am looking at something like the following modal that has both the rating types: (Question marks deliberate to indicate that this is not the final design)
trait? ProductRating( rating: Int, productId: Int, userId: Int )
{
val sqlTable
def getRatings: ProductRating = doMagicSql( s"SELECT * FROM $sqlTable" )
}
case Class? WorksFineRating(rating, productId, userId) extends Rating( rating, productId, userId){ val sqlTable = "WorksFineRating" }
case Class? PriceOkayRating(rating, productId, userId) extends Rating( rating, productId, userId){ val sqlTable = "PriceOkayRating" }
Here's how I'm actually modelling it in Scala:
trait ProductRating[ PR <: ProductRating[ PR ] ]{
val reviewId: Int
val userId: Int
val rating: Int
}
case class WorksFineRating(
reviewId: Int,
userId: Int,
rating: Int
) extends ProductRating[ WorksFineRating ]
case class PriceOkayRating(
reviewId: Int,
userId: Int,
rating: Int
) extends ProductRating[ PriceOkayRating ]
trait ProductRatingCompanion[ PR <: ProductRating[ PR ] ]
{
val sqlTable: String
def factory( reviewId: Int, userId:Int, rating:Int ): PR
def getAllFromSqlTable = {
Seq( factory( 1, 2, 3 ), factory( 4, 5, 6 ) )
}
}
object WorksFineRating
extends ProductRatingCompanion[ WorksFineRating ]
{
val sqlTable = "WorksFineRating"
def factory( reviewId: Int, userId:Int, rating:Int )
: WorksFineRating =
WorksFineRating( reviewId, userId, rating )
}
object PriceOkayRating
extends ProductRatingCompanion[ PriceOkayRating ]
{
val sqlTable = "PriceOkayRating"
def factory( reviewId: Int, userId:Int, rating:Int ): PriceOkayRating =
PriceOkayRating( reviewId, userId, rating )
}
object T extends App{
println( WorksFineRating.getAllFromSqlTable )
}
Explanations:
1) I was not very comfortable putting sqlTable inside the class as I showed in the first code outline. I think not every instance should have to know this. (I guess rest seems straightforward to me).
Questions:
1) Am I overcomplicating by adding FBounded polymorphism to the ProductRating trait? It's not required there. It is indeed required in the ProductRatingCompanion object because factory's return type is known to be PR. Is it not?
2) Can I somehow get rid of the 'factory'? The code is almost identical. I don't understand JVM all that much, but if an external library were to use this class, it would see only the generic version of the companions, which means that at runtime, there would be no way to create an instance of that class. Is that correct? Can I somehow store the type in the object? Like type T = RR.type? (It doesn't work)
3) How does one create a function that can handle both types? Like a function that takes a param "PriceOrFunctionality" and decides to create an instance of appropriate class. If you do an if-else, then the type of this instance would actually be a mixed type of the two types. So any further operations using companion object would require asInstanceOf calls.
Any suggestions would be great. I'd like for this question to be a good reference for design- so I'm willing to make a lot of modifications and make it community wiki (I'm not looking to gain points here) so that people can use this as an example of good OO design. So I'd appreciate if you guys could help. (I'll find out how to make community wiki)
EDIT: I can't make this question a community wiki. If any mods could do that, that would be great. Thanks in advance.