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
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
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
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
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
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,
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.
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.