Ответ 1
Технически все, что можно реализовать с наследованием, может быть реализовано и с делегированием. Таким образом, ответ будет "нет".
Преобразование наследования в делецию
Скажем, у нас есть следующие классы, реализованные с наследованием:
public class A {
String a = "A";
void doSomething() { .... }
void getDisplayName() { return a }
void printName { System.out.println( this.getDisplayName() };
}
public class B extends A {
String b = "B";
void getDisplayName() { return a + " " + b; }
void doSomething() { super.doSomething() ; ... }
}
Материал работает красиво, а вызов printName
в экземпляре B будет печатать "A B"
в консоли.
Теперь, если мы перепишем это с делегированием, получим:
public class A {
String a = "A";
void doSomething() { .... }
void getDisplayName() { return a }
void printName { System.out.println( this.getName() };
}
public class B {
String b = "B";
A delegate = new A();
void getDisplayName() { return delegate.a + " " + b; }
void doSomething() { delegate.doSomething() ; ... }
void printName() { delegate.printName() ; ... }
}
Нам нужно определить printName
в B, а также создать делегат при создании экземпляра B. Вызов doSomething
будет работать так же, как и с наследованием. Но вызов printName
будет печатать "A"
в консоли. Действительно, с делегацией мы потеряли мощную концепцию "this", связанную с экземпляром объекта и базовыми методами, способными вызывать методы, которые были отменены.
Это можно решить на языке, поддерживающем чистое делегирование. При чистое делегирование "this" в делегате по-прежнему будет ссылаться на экземпляр B. Это означает, что this.getName()
начнет отправку метода из класса B. Мы достигнем того же, что и с наследованием. Это механизм, используемый в prototype-based, например Self, у которых есть делегирование, имеет встроенную функцию (вы можете прочитать здесь, как наследование работает в Self).
Но Java не имеет чистого делегирования. Когда они застряли? Нет, мы все еще можем сделать это с еще большим усилием:
public class A implements AInterface {
String a = "A";
AInterface owner; // replace "this"
A ( AInterface o ) { owner = o }
void doSomething() { .... }
void getDisplayName() { return a }
void printName { System.out.println( owner.getName() };
}
public class B implements AInterface {
String b = "B";
A delegate = new A( this );
void getDisplayName() { return delegate.a + " " + b; }
void doSomething() { delegate.doSomething() ; ... }
void printName() { delegate.printName() ; ... }
}
Мы в основном реинжинируем то, что обеспечивает встроенное наследование. Имеет ли это смысл? Нет. Но это иллюстрирует, что наследование всегда может быть преобразовано в делецию.
Обсуждение
Наследование характеризуется тем, что базовый класс может вызывать метод, который переопределяется в подклассе. Это, например, суть шаблона шаблона . С делегацией такие вещи нелегко сделать. С другой стороны, это именно то, что делает наследование трудным в использовании. Для этого требуется ментальный поворот, чтобы понять, где происходит полиморфная отправка, и каков эффект, если методы переопределены.
Есть некоторые известные подводные камни о наследовании и хрупкости, которые он может внести в дизайн. Особенно, если иерархия классов развивается. Также могут возникнуть проблемы с equality в hashCode
и equals
, если используется наследование. Но с другой стороны, это еще очень элегантный способ решения некоторых проблем.
Кроме того, даже если наследование может быть заменено делегированием, можно утверждать, что они все еще достигают различной цели и дополняют друг друга - они не передают одно и то же намерение, которое не захватывается чистой технической эквивалентностью.
(Моя теория заключается в том, что, когда кто-то начинает делать OO, у нас возникает соблазн чрезмерно использовать наследование, потому что оно воспринимается как особенность языка. Затем мы изучаем делегирование, которое является шаблоном/подходом, и мы также учимся любить его. Через некоторое время мы находим баланс между обоими и развиваем чувство интуиции, из которого лучше в этом случае. Ну, как вы можете видеть, мне все еще нравятся оба, и оба заслуживают некоторой осторожности перед тем, как их ввести.)
Некоторая литература
Наследование и делегирование альтернативные методы для инкрементного определения и совместного использования. В нем есть обычно считалось, что делегация обеспечивает более мощную модель. Эта бумага показывает, что существует "естественная" модель наследования, которая захватывает все свойства делегация. Независимо определенные ограничения на способность делегирование для наследования продемонстрировал. Наконец, новая структура который полностью отражает обе делегации и наследование очерчено, а некоторые последствий этого гибрида модель.
Один из самых интригующих - и на в то же время большинство проблемных понятий в объектно-ориентированное программирование наследование. Наследование обычно рассматривается как функция, которая отличает объектно-ориентированную программирование из других современных парадигмы программирования, но исследователи редко соглашаются на его значение и использование. [...]
Из-за сильной связи классов и распространения ненужных членов класса, индуцированных по наследованию, предложение использовать состав и делегирование вместо этого стало обычным явлением. Презентация соответствующего рефакторинга в литературе может привести к тому, что такое преобразование является прямым делом. [...]