ScalaKnol: Understanding Traits as Stackable Modifications


Traits can be used to modify the methods of a class. The way such modifications are carried out is such that they can stack modifications with each other.

Let us take an example. Say, there is a class called ImageProcessor which processes images. Now there are a few modifications required before the image is actually uploaded to your server. If you are using Facebook then for every image uploaded, it does a few things on the image which might include i) compressing the image ii) scaling that in various sizes iii) extracting a snapshot of the image of the video etc. It might do a ton of other things but let us keep it simple for now.

So lets see some code

  abstract class ImageProcessor { def process(name: String) = println("Base image processing") }
  class ImageProcessorImpl extends ImageProcessor { override def process(name: String) = super.process("myImage") }

So, ImageProcessor is the abstract class and ImageProcessorImpl is the concrete class which provides implementation. Calling a process on the implementation gives us the following result

new ImageProcessorImpl process("image1")          //> Base image processing

Now, what if we want to add modifications to the base image processing, which would say cut a frame out of that image. We define a trait called FrameCutter which does the following

 trait FrameCutter extends ImageProcessor {
    abstract override def process(name: String) = {
      println("Cutting frame"); super.process(name)
    }
  }

We are doing an abstract override here. Such a combination is only possible in traits. What we are doing here is that we are telling the compiler that we are defining a trait which would always be mixed with another trait or class which would provide the concrete implementation of process.

Let us define another trait which compresses the image which is uploaded to the server

  trait ImageCompressor extends ImageProcessor {
    abstract override def process(name: String) = {
      println("Compressing Image"); super.process(name)
    }
  }

Now, these 2 traits describe modifications. They modify the bahavior of the process method and they can be stacked to give stackable modifications. So, let us say we need an object that first “cuts a frame” and then “compresses the image” and then “does the base processing”. Such an object would be defined like

(new ImageProcessorImpl with ImageCompressor with FrameCutter).process("image1")
                                                  //> Cutting frame
                                                  //| Compressing Image
                                                  //| Base image processing

So, we mixed the traits with a concrete class which provides the definition of process. The interesting thing to note here is that with traits, super is dynamically bound on the basis of how the trait is mixed. When a method is called on a trait then the method is called on the trait which is further right in the mix. If this method calls super then it invokes this method on the trait on the left and so,on. In the above example, FrameCutter was the last trait (furthest to the right) which was mixed in. It is called and then the ImageCompressor and then the Concrete class.

Super for classes is always determined statically. Hence for the following code,

  abstract class ImageProcessor { def process(name: String) = println("Base image processing") }
  class ImageProcessorImpl extends ImageProcessor { override def process(name: String) = super.process("myImage") }

super for ImageProcessorImpl is always ImageProcessor.

Ok, coming back to our stackable modifications. What would you do if we need to compress the image first and then cut it? We would mix it the other way i.e.

(new ImageProcessorImpl with FrameCutter with ImageCompressor).process("image1")
                                                  //> Compressing Image
                                                  //| Cutting frame
                                                  //| Base image processing

So we get modifications in traits and we can stack them the way we want to get the desired results. The key things to remember are
i) Behavior of super in traits
ii) Declaring methods with abstract override in traits
iii) Keeping in mind the order of mixin.

About these ads

About Vikas Hazrati

Vikas is the CTO @ Knoldus which is a group of software industry veterans who have joined hands to add value to the art of software development. We do niche product and project development on Scala and Java. We consult and coach on effective software development and agile practices. With our focus on software craftsmanship you can be assured of a good quality at the right price. To know more, send a mail to info@knoldus.com or visit www.knoldus.com
This entry was posted in Scala and tagged , , . Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s