На занятии по языкам программирования мой профессор цитирует примеси как одно из решений проблемы хрупкого базового класса. Википедия также использовала примеси (Ruby) как решение проблемы хрупкого базового класса, но некоторое время назад кто-то удалил ссылку на миксины. Я все еще подозреваю, что они могут каким-то образом иметь преимущество перед наследованием в отношении проблемы хрупкого базового класса. Иначе с чего бы профессору говорить, что они помогают?
Приведу пример возможной проблемы. Это простая реализация задачи (Java) на языке Scala, которую профессор дал нам для иллюстрации проблемы.
Рассмотрим следующий базовый класс. Предположим, что это какая-то очень эффективная специальная реализация списка, и что над ним определено больше операций.
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)
}
}
}
Также обратите внимание на следующую черту, которая должна добавить size
к приведенному выше списку.
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 }
}
Проблема в том, что сработает трейт или нет, зависит от того, как мы реализуем addAll
в базовом классе, т. е. функциональность, предоставляемая базовым классом, «хрупка», как и в случае с обычным extends
в Java или любом другом. язык программирования.
Например, если мы запустим следующий код с MyList
и CountingSet
, как определено выше, мы получим обратно 5
, тогда как мы ожидали получить 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
}
}
Если мы изменим addAll
в базовом классе (!) следующим образом, трейт CountingSet
будет работать как положено.
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
}
}
}
Имейте в виду, что я совсем не эксперт по Scala!