1

I have a class which represents posts in our system. Where a post might represent a question, document, image, etc. There are about 7 different types of objects the Post class can represent. Each of the 7 different types of objects we have has it's own metadata class to store additional object specific information.

Currently my Post class has 7 optional attributes, one of which gets filled depending on the type of object it is. But since the Post class will only ever have one of these 7 attributes filled, is there a way to consolidate these into a single attribute with an arbitrary type? Then I could use a match case statement to generate the correct metadata object at runtime. Or this impossible with Scala given the strongly typed nature of the language.

Code is below:

 case class Post ( id : Long, typ : String, name : String, fileInfo : Option[FileInfo], imageInfo : Option[FileImageInfo], videoInfo : Option[FileVideoInfo], audioInfo : Option[FileAudioInfo], eventInfo: Option[EventInfo], lectureInfo: Option[LectureInfo], drawingInfo: Option[DrawingInfo] ) object Post { val simple = { get[Long]("object_view.id") ~ get[String]("object_view.type") ~ get[String]("object_view.name") map { case id~typ~name => Post( id, typ, name, FileInfo.getById(id), FileImageInfo.getById(id), FileVideoInfo.getById(id), FileAudioInfo.getById(id), EventInfo.getFirst(id), LectureInfo.getById(id), DrawingInfo.getById(id) ) } } 
1
  • Using a String to represent type seems like a bad idea. Why not have several types implement a common Post interface, like so? trait Post { def id: Long def name: String } case class FilePost(id:Long, name:String, info: FileInfo) extends Post { } Commented Dec 18, 2012 at 1:39

3 Answers 3

1

Why not make Post abstract, then implement a subclass for each different type of post ? Something like:

 abstract class Post { val id:Long; val typ:String; val name:String; } case class FilePost( id : Long, typ : String, name : String, fileInfo : Option[FileInfo ); case class ImagePost( id : Long, typ : String, name : String, imageInfo : FileImageInfo ); ... def doSomething( post:Post ):Unit = post match { case fp:FilePost => ... } 

Doh! - looks like earlier response said the same thing ...

Sign up to request clarification or add additional context in comments.

Comments

1

Well, here in 2022, what I would do is very similar to what Reuben did, but I'd move the metadata into a separate ADT. Like this

sealed trait PostMetadata object PostMetadata { final case class File(val info: FileInfo) extends PostMetadata final case class Image(val info: FileImageInfo) extends PostMetadata final case class Video(val info: FileVideoInfo) extends PostMetadata final case class Audio(val info: FileAudioInfo) extends PostMetadata final case class Event(val info: EventInfo) extends PostMetadata final case class Lecture(val info: LectureInfo) extends PostMetadata final case class Drawing(val info: DrawingInfo) extends PostMetadata } final case class Post( val id: Long, val name: String, val metadata: PostMetadata ) object Post { val simple = get[Long]("object_view.id") ~ get[String]("object_view.type") ~ get[String]("object_view.name") map { case id ~ typ ~ name => Post( id, name, typ match { case "file" => PostMetadata.File(FileInfo.getById(id).get) case "image" => PostMetadata.Image(FileImageInfo.getById(id).get) case _ => // and so on } ) } } 

This way you can match against post.metadata and actually get a resolved metadata value on info instead of having to check for an Option.

If you want to able to export type back you may want to add a def typ: String on the trait and implement on each of the case classes, or just adding typ and matching self against each of the case classes. Also, you could have Map[String, Long => PostMetadata] to simplify that match down there.

Comments

0
class FileInfo(val name: String) abstract trait CanGet[T] { val value: Option[T]; def get = value.get } case class PostFileInfo(val id: Long, val typ: String, val name: String) extends { val value = Some(new FileInfo(name)) } with CanGet[FileInfo] ... (1L, "FileInfo", "FileName") match { case (id, typ @ "FileInfo", name) => new PostFileInfo(1, typ, name) } 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.