★ To Mix in or not to Mix in
— 1 minute read
In his article ”Composition over Mixins” Eric Meyer makes the case why Mixins should not be used, because a Mixin is
a violation of the SRP by definition.
On the hand, he is right. By using Mixins, different functionality is integrated into one class or object. At least at run time. On the other hand, Mixins actually help to separate responsibilities to different parts at the source code level and thus, at compile time.
For example, the following excerpt from a log processor in Scala consists of an interface LogProcess (line 1) and two* implementations *LogDumper (line 12) and LogHBaseAdder (line 19).* *The first just prints the log entry to the console (line 14) and the second adds it to an HBase table (line 21). These two implementations are mixed into the default implementation of the interface creating a single object (lines 28-29) that supports both, printing and adding.
abstract class LogProcessor( val filename: String ) {
def process() : Unit = {
Source.fromFile( filename ).
getLines.map.( line =>
processEntry( LogEntry(line) ) )
}
protected def processEntry( logEntry: LogEntry ) : Unit = {}
}
trait LogDumper extends LogProcessor {
abstract override protected def processEntry( logEntry: LogEntry ) : Unit = {
println( logEntry.toString )
super processLogEntry logEntry
}
}
trait LogHBaseAdder extends LogProcessor {
abstract override protected def processEntry( logEntry: LogEntry ) : Unit = {
htable put logEntry.createPut( family )
super processLogEntry logEntry
}
}
object Runner {
def main( args: Array[String] ) {
val lp = new LogProcessor( args(0) )
with LogDumper with LogHBaseAdder
lp.process
}
}
Of course, this is also possible using composition. But using Mixins, both, LogDumper and LogHBaseAdder, are not aware of each other. Even the interface does not need to know about this. So we do achieve a separation of responsibility and only wire the functionality together when creating the object.
So maybe Mixins are not that bad at all.