In a class on programming languages, my professor quotes mixins as one of the solutions to the fragile base class problem. Wikipedia also used to list (Ruby) mixins as a solution for the fragile base class problem, but some time ago someone removed the reference to mixins. I still suspect that they might somehow have an advantage over inheritance with regards to the fragile base class problem. Otherwise, why would a professor say that they help?
I'll give an example of a possible problem. This is a simple Scala implementation of the (Java) problem that the professor gave us to illustrate the problem.
Consider the following base class. Assume that this is some very efficient special implementation of a list, and that more operations are defined on it.
class MyList[T] {
private var list : List[T] = List.empty
def add(el:T) = {
list = el::list
}
def addAll(toAdd:List[T]) : Unit = {
if (!toAdd.isEmpty) {
add(toAdd.head)
addAll(toAdd.tail)
}
}
}
Also consider the following trait, which is supposed to add size
to the list above.
trait CountingSet[T] extends MyList[T] {
private var count : Int = 0;
override def add(el:T) = {
count = count + 1
super.add(el)
}
override def addAll(toAdd:List[T]) = {
count = count + toAdd.size;
super.addAll(toAdd);
}
def size : Int = { count }
}
The problem is that whether or not the trait will work depends on how we implement addAll
in the base class, i.e. the functionality provided by the base class is 'fragile', just as would be the case with a regular extends
in Java or any other programming language.
For example, if we run the following code with MyList
and CountingSet
as defined above, we get back 5
, whereas we would expect to get 2
.
object Main {
def main(args:Array[String]) : Unit = {
val myCountingSet = new MyList[Int] with CountingSet[Int]
myCountingSet.addAll(List(1,2))
println(myCountingSet.size) // Prints 5
}
}
If we change addAll
in the base class (!) as follows, the trait CountingSet
works as expected.
class MyList[T] {
private var list : List[T] = List.empty
def add(el:T) = {
list = el::list
}
def addAll(toAdd:List[T]) : Unit = {
var t = toAdd;
while(!t.isEmpty) {
list = t.head::list
t = t.tail
}
}
}
Keep in mind that I am anything but a Scala expert!