Reputation: 1096
I have a base trait
trait MyClass{
def items: List[MyClass]
def op(c: MyClass): MyClass
}
and several concrete subtypes.
I want each of the subtypes to implement this method op
from the parent trait, but the only difference in the implementation is which class they are constructing.
class Class1(override val items: List[MyClass]) extends MyClass = {
def op(c: MyClass): MyClass = new Class1(items.map{_.op(c)})
//other stuff
}
class Class2(override val items: List[MyClass]) extends MyClass = {
def op(c: MyClass): MyClass = new Class2(items.map{_.op(c)})
//other stuff
}
class Class3(override val items: List[MyClass]) extends MyClass = {
def op(c: MyClass): MyClass = new Class3(items.map{_.op(c)})
//other stuff
}
This feels pretty redundant and I feel as though there should be a better way. I've looked at some similar situations, and it seems that people resort to runtime reflection to find the right constructor.
Is there anyway to automatically generate this boilerplate at compile time?
Thanks.
Upvotes: 2
Views: 92
Reputation: 452
Use the Template Method design pattern: https://www.tutorialspoint.com/design_pattern/template_pattern.htm
In your trait, implement op() with a call to a protected method.
def op(c: MyClass): MyClass = createObject(items.map{_.op(c)})
protected def createObject(inItems: List[MyClass]): MyClass
Then, in the subtypes, implement createObject() so that it returns the right object:
override def createObject(inItems: List[MyClass]) = new Class1(inItems)
Replacing Class1
as needed, of course.
It's not completely autogenerated, but there is less duplication.
Upvotes: 2