Mark is a graph advocate and field engineer for Neo Technology, the company behind the Neo4j graph database. As a field engineer, Mark helps customers embrace graph data and Neo4j building sophisticated solutions to challenging data problems. When he's not with customers Mark is a developer on Neo4j and writes his experiences of being a graphista on a popular blog at http://markhneedham.com/blog. He tweets at @markhneedham. Mark is a DZone MVB and is not an employee of DZone and has posted 524 posts at DZone. You can read more from them at their website. View Full User Profile

Scala: Traits galore

07.10.2011
| 4350 views |
  • submit to reddit

We recently came across a problem where we had some logic that we wanted to be used by two classes.

Our original thought was to pull it up into an abstract class which ended up looking like this:

abstract class SomeArbitraryClass(root:xml.Node) {
  def unrelatedField1:String
  def unrelatedField2:String
 
  def startPage:String
  def endPage:String
  def pageRange = if(firstPage == lastPage) "page %s".format(firstPage) else "pages %s-%s".format(firstPage, lastPage)
}

Writing a test *link to scala test* for the page logic helped us to see more clearly that the design was a bit awkward:

class SomeArbitraryClassTest extends Spec with ShouldMatchers {
  it("should format page range") {
    val someArbitraryClass = new SomeArbitraryClass(<empty />) {
      def unrelatedField1 = null
      def unrelatedField2 = null
      def startPage = "1"
      def endPage = "2"
    }
 
    someArbitraryClass.pageRange should equal("pages 1-2")
  }
}

It seemed pretty weird to have to pass in an empty XML tag to the class when our test didn’t care at all about XML.

Having unrelatedField1 and unrelatedField2 as null values further helped us to see that the page stuff is totally unrelated to the rest of this class.

We therefore pulled out a trait just for the three page related functions:

trait PageAware {
  def startPage:String
  def endPage:String
  def pageRange = if(firstPage == lastPage) "page %s".format(firstPage) else "pages %s-%s".format(firstPage, lastPage)
}

That trait is much more cohesive than the original Content class and our new test reflects that:

class PageAwareTest extends Spec with ShouldMatchers {
  it("should format page range") {
    val pageAware = new PageAware() {
      def startPage = "1"
      def endPage = "2"
    }
 
    pageAware.pageRange should equal("pages 1-2")
  }
}

And we can pull that trait into the abstract class too:

abstract class SomeArbitraryClass(root:xml.Node) extends PageAware {
  def unrelatedField1:String
  def unrelatedField2:String
}

I guess we could also just pull in PageAware at the sub class level but since all of the sub classes need to be page aware this seems to make sense.

We seem to be pulling out traits quite frequently as they seem to be a really nice way to abstract a group of related functions.

Our design is starting to resemble the cake layering pattern suggested by Martin Emde as an approach for avoiding ActiveRecord objects becoming cluttered with logic in Rails land.

 

From http://www.markhneedham.com/blog/2011/07/09/scala-traits-galore/

Published at DZone with permission of Mark Needham, author and DZone MVB.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Comments

Kai Wähner replied on Mon, 2011/07/11 - 3:04am

Traits are my favorite feature in Scala.They are easy to understand, do solve the "diamond problem" of multiple inheritance and give a lot of opportunities to seperate and structure code.

I would appreciate if Traits make it to Java. That would be much more important than Closures and all this stuff (because e.g. Closures will be ugly in Java, no matter which syntax will finally be chosen)...

 

Best regards, 

Kai Wähner (Twitter: @KaiWaehner)

Tomasz Nurkiewicz replied on Mon, 2011/09/05 - 2:26pm

Not sure, but seems like there should have been a link to ScalaTest in place labeled *link to scala test* :-).

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.