Ryan Stull
Ryan Stull

Reputation: 1096

How to scrap boilerplate for scala class construction?

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

Answers (1)

Henrik
Henrik

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

Related Questions