Здравствуйте, <Аноним>, Вы писали:
А>в чем собственно принципиальная разница?
Расширение — это прямое наследование. Такоим образом ты получаешь такие преимущества как полиморфизм, позднее связывание и наследование базовой функциональности. Аггрегирование — это включение объектов в состав другого объекта.
Вообще говоря, время жизни аггрегированного объекта, равно времени жизни аггрегата (объекта-владельца)
А>в каких случаях что лучше использовать?
Соответственно, наследование используем для представления связи типа родитель-потомок (вид-подвид и т.д.). Аггрегирование используем для представления связей типа целое-часть.
... << RSDN@Home 1.1.4 @@subversion >>
Здравствуйте, Аноним, Вы писали:
А>в чем собственно принципиальная разница?
А>в каких случаях что лучше использовать?
Это не совсем ответ на твой вопрос, но раз уж речь пошла о наследовании, будет полезно почитать
Why extends is evil.
А>>в чем собственно принципиальная разница?
A_G>Расширение — это прямое наследование. Такоим образом ты получаешь такие преимущества как полиморфизм, позднее связывание и наследование базовой функциональности.
A_G>Аггрегирование — это включение объектов в состав другого объекта.
A_G>Вообще говоря, время жизни аггрегированного объекта, равно времени жизни аггрегата (объекта-владельца)
Вообще говоря нет — различают два вида аггрегации: собственно аггрегацию и композицию.
Все начинается с ассоциации, которая говорит о том, что между объектами двух классов можно построить некоторое отношение.
Различают отношения 1->1 (один к одному), 1->N (один ко многим) и N->M (многие ко многим). Ассоциация как правило реализуется через ссылку на объект или через коллекцию.
Аггрегация является частным случаем ассоциации, при котором имеет место отношение "часть-целое" (то самое включение объектов в состав другого объекта), композиция — аггрегация, при которой композит несет отвественность за хранение и жизненный цикл аггрегированного объекта.
Пример ответственности за хранение: при хранении в памяти в аггрегате хранится только ссылка на аггрегированный объект, а в композите — собственно содержимое аггрегированного объекта. Пример синтетический, в том или ином виде реализуется для старых компиляторов С++.Отсюда, также, растет странное поведение Rational Rose, где для создания композиции нужно создать аггрегацию (здесь все верно) и установить вместо хранения по ссылке хранение по значению (что, собственно говоря, уже неверно, т.к. в той же Java не бывает хранения по значению).
Аггрегация допускает построение отношений N->M, когда как композиция — только 1->N (иначе нельзя соблюсти ограничение на время жизни аггрегированного объекта).
Данное описание изложено в спецификации OMG UML. Сообщение надо расценивать исключительно как терминологическое уточнение

.
Здравствуйте, Аноним, Вы писали:
А>в чем собственно принципиальная разница?
А>в каких случаях что лучше использовать?
Ниже я буду называть расширение наследованием (это более
Обычно лучше использовать аггрегирование, потому как наследование — отношение достаточно жесткое, ограничивающее в .
Для получения хорошего дизайна необходимо, чтобы выполнялся принцип подстановки, сформулированный Барбарой Лисков (
Liskov Substitution Principle, LSP), который гласит, что S является подклассом T только тогда, когда любые программы, написанные с использованием объектов класса T будут иметь то же самое поведение, если все объекты класса T заменить объектами класса S.
Хрестоматийным примером неоправданного наследования стали квадрат и прямоугольник:
Рассмотрим класс прямоугольника со следующим контрактом:
class Rectangle()
{
private int w;
private int h;
public Rectangle(int w, int h)
{
setWidth(w);
setHeight(h);
}
public int getWidth()
{
return w;
}
public int getHeight()
{
return h;
}
public void setHeight(int h)
{
this.h = h;
}
public void setWidth(int w)
{
this.w = w;
}
}
Затем создадим класс Square, как прямоугольник с равными сторонами. Кроме создания специяического конструктора нам нужно будет перегрузить методы setWidth и setHeight с тем, чтобы ыполнялось правило w == h.
class Square
{
public Square (int a)
{
super(a,a);
}
public void setHeight(int h)
{
super.setHeight(h);
super.setWidth(h);
}
public void setWidth(int w)
{
super.setHeight(w);
super.setWidth(w);
}
}
Казалось бы все замечательно, но представим себе следующий тест:
public class SquareTest
{
public void doTest(Rectangle r)
{
r.setWidth(5);
r.setHeight(6);
if (30 != r.getWidth()*r.getHeight())
{
throw new RuntimeException("Square test failed");
}
}
}
Из которого видно, что наследование здесь неправомерно (тест сломается для экземпляра класса Square). Продобное применение наследования ведет к соверженно неоправданному использованию reflection (в т.ч. instanceof). Поэтому с наследованием нужно обращаться очень осторожно — оно является поведенческим отношением, а не какой-то общностью, взятой из реального мира. К примеру можно построить такой поведенческий контракт прямоугольника, при котором квадрат будет являться прямоугольником (например, выбросив методы setWidth и setHeight из класса Rectangle), но в приведенном случае Square не должен быть наследником Rectangle.
Есть, правда, два исключения:
1) Если приходится расширять framework, который предполагает использование наследования (даже не самым правильным образом). Тут деваться некуда.
2) Если нужно в нескольких классах получить какой-то базовый набор полей (главным образом) и алгоритмов (читай — protected методов). Но и в этом случае стоит пользоваться наследованием до тех пор, пока возможно наследование базового набора путем наследования класса с пакетной видимостью. Т.е. все
прямые пользователи базы —
классы первого уровня (e .g. наследники java.lang.Object), использующие тот самый общий набор полей и алгортимов, — лежат в одном пакете. (В с++ в таких случаях стоит использовать protected и private наследование).
Если это не так, то зачастую бывает правильнее скопировать этот базовый набор по числу пакетов, где лежат
прямые пользователи базы. По моему опыту неправильное наследование обходится в поддержке дороже, чем дублирование идентичного кода по пакетам.
Если нужно иметь только набор методов, то правильнее вынести его в отдельный класс и аггрегировать. Или, возможно, сделать эти методы статическими.
Основное правило —
если между классами нет поведенческой общности — лучше использовать аггрегацию и
иногда даже дублирование кода (
!), потому как при правильном подходе к дублированию, оно будет менее болезненным, чем неправомерное наследование.
Плюсы наследования
изложилАвтор: A_Gura
Дата: 18.01.05
A_Gura
Также, насколько я понимаю, в статье на которую
дал ссылкуАвтор: dshe
Дата: 18.01.05
dshe изложены схожие соображения.