что такое композиция java
Чем отличаются наследование и композиция в Java
Авторизуйтесь
Чем отличаются наследование и композиция в Java
Несмотря на то, что и композиция, и наследование позволяют использовать код повторно, они делают это по-разному. Основное отличие между ними состоит в том, что композиция позволяет переиспользовать код без его расширения. Наследование при этом требует расширения существующего класса. Другое важное отличие: при композиции мы можем повторно использовать код даже из final-класса, тогда как унаследоваться от него невозможно. Кроме того при композиции возможно использование кода из нескольких различных классов. С наследованием такой трюк не сработает: в Java не поддерживается множественное наследование. Зато вы можете сделать это в C++. Кстати, всегда стоит предпочитать композицию наследованию в Java. И не только потому, что так советует Джошуа Блох в своей книге Effective Java (которая является отличным источником полезных советов для Java-программиста). Аргументы за использование композиции вы можете прочитать здесь.
Наследование и композиция
Давайте посмотрим более детально на отличия композиции и наследования. Мы рассмотрим каждый пункт достаточно подробно, но без скучных деталей.
Гибкость
Первое отличие касается гибкости кода. При использовании наследования вы должны описать, какой класс вы расширяете. При этом, вы не можете заменить его во время выполнения программы. Используя композицию, вы можете определить используемый тип, который может содержать несколько различных реализаций. Таким образом, композиция предоставляет гораздо больше гибкости, чем наследование.
Ограниченное повторное использование кода при наследовании
Как уже было сказано, унаследоваться в Java можно только от одного класса, а это значит, что повторно можно использовать один, и только один класс. Если вы хотите получить функциональность нескольких классов, то вам следует использовать композицию. К примеру: ваш код должен использовать аутентификацию, следовательно вам надо унаследовать класс Authenificator, для использования авторизации — Autorizer и т. д. Но, поскольку множественное наследование в Java не поддерживается, вы этого сделать не можете. Единственный выход — композиция.
Юнит-тесты
Важный аргумент при выборе способа повторного использования кода состоит в том, что классы, расширенные при помощи композиции, легче тестировать, поскольку вы всегда можете предоставить заглушку для используемого класса. При наследовании вам потребуется родительский класс для тестирования, и заменить его заглушкой не получится.
Final-классы
Невозможность расширить final-классы — еще одно ограничение наследования. В Java нет возможности унаследоваться от final-классов, поэтому опять единственный выход — использовать композицию для повторного использования кода.
Инкапсуляция
Последнее отличие композиции и наследования в Java заключается в их отношении к инкапсуляции. Несмотря на то, что и та, и другая техники позволяют повторно использовать код, наследование нарушает принцип инкапсуляции, поскольку подкласс зависит от поведения родительского класса. Если класс-родитель изменит свое поведение, это повлияет и на его потомков. Если классы плохо документированы и класс-потомок неправильно использует родительский, то при любом изменении родителя функциональность потомка окажется сломанной. Для того, чтобы увидеть это на примере, вы можете прочитать главы 16 и 17 Effective Java.
Вот и все, что можно сказать об отличиях композиции и наследования в Java и в объектно-ориентированном программировании вообще. Как видите, они по-разному служат одной цели: повторное использование проверенных и протестированных участков кода. Композиция при этом позволяет защитить повторно используемый класс от клиентов, тогда как наследование этого не гарантирует. Тем не менее, иногда наследование необходимо. В частности, когда вы создаете классы из одного семейства.
Композиция в примере Java
Композиция в Java реализует отношения между объектами. Что такое композиция на Java? Пример программы для составления Java и видеоурок.
Композиция в java-это метод проектирования для реализации отношений has-a в классах. Мы можем использовать наследование java или композицию объектов в java для повторного использования кода.
Композиция на языке Java
Пример композиции Java
Вот пример тестового класса для java-композиции, который использует объект person и получает его зарплату.
Преимущества композиции Java
Преимущество использования композиции в java заключается в том, что мы можем контролировать видимость других объектов для клиентских классов и повторно использовать только то, что нам нужно.
Также, если есть какие-либо изменения в реализации другого класса, например getSalary возвращающая строка, нам нужно изменить класс Person, чтобы приспособить его, но клиентские классы не нуждаются в изменении.
Композиция позволяет создавать внутренний класс, когда это необходимо, например, мы можем изменить Person | getSalary метод для инициализации объекта задания во время выполнения, когда это необходимо.
Java Композиция Видео на Youtube
Недавно я создал видео на YouTube, чтобы подробно объяснить композицию на java, пожалуйста, посмотрите его ниже.
Это все для композиции в java или примере композиции java. Я надеюсь, что вы найдете это полезным для принятия обоснованных решений при разработке классов приложений.
Композиция, агрегация и ассоциация в Java
Изучите свойства и представление композиции, агрегации и ассоциации в Java.
1. введение
Объекты имеют отношения между собой, как в реальной жизни, так и в программировании. Иногда трудно понять или реализовать эти отношения.
В этом уроке мы сосредоточимся на подходе Java к трем иногда легко смешиваемым типам отношений: композиция, агрегация и ассоциация.
2. Состав
В качестве альтернативы, мы часто называем это отношением “имеет-а” (в отличие от отношения “есть-а”, которое является наследованием ).
Например, комната принадлежит зданию, или, другими словами, в здании есть комната. Таким образом, в основном, называем ли мы это “принадлежит” или “имеет”, это только вопрос точки зрения.
Композиция-это сильный вид отношений “имеет-а”, потому что содержащий объект владеет им. Таким образом, жизненные циклы объектов связаны. Это означает, что если мы уничтожим объект-владельца, его члены также будут уничтожены вместе с ним. Например, комната разрушается вместе со зданием в нашем предыдущем примере.
Обратите внимание, что это не означает, что содержащий объект не может существовать без какой-либо из его частей. Например, мы можем снести все стены внутри здания, следовательно, уничтожить комнаты. Но здание все равно будет существовать.
2.1. UML
В UML мы обозначаем композицию следующим символом:
Обратите внимание, что алмаз находится в содержащем его объекте и является основанием линии, а не наконечником стрелы. Для ясности мы тоже часто рисуем наконечник стрелы:
Итак, мы можем использовать эту конструкцию UML для нашего примера здания:
2.2. Исходный код
В Java мы можем смоделировать это с помощью нестатического внутреннего класса:
В качестве альтернативы мы также можем объявить этот класс в теле метода. Не имеет значения, является ли это именованный класс, анонимный класс или лямбда:
Обратите внимание, что очень важно, чтобы наш внутренний класс был нестатическим, поскольку он связывает все свои экземпляры с содержащим классом.
Обычно содержащий объект хочет получить доступ к своим членам. Поэтому мы должны хранить их ссылки:
Обратите внимание, что все внутренние объекты класса хранят неявную ссылку на содержащийся в них объект. В результате нам не нужно хранить его вручную, чтобы получить к нему доступ:
3. Агрегация
Агрегация также является отношением “есть-есть”. Что отличает его от композиции, так это то, что он не предполагает владения. В результате жизненный цикл объектов не привязан: каждый из них может существовать независимо друг от друга.
Например, автомобиль и его колеса. Мы можем снять колеса, и они все еще будут существовать. Мы можем установить другие (уже существующие) колеса или установить их на другой автомобиль, и все будет работать просто отлично.
3.1. UML
Агрегация очень похожа на композицию. Единственное логическое различие заключается в том, что агрегация-это более слабая связь.
Поэтому представления UML также очень похожи. Разница лишь в том, что алмаз пуст:
Тогда для автомобилей и колес мы бы сделали:
3.2. Исходный код
В Java мы можем моделировать агрегацию с помощью простой старой ссылки:
Членом может быть любой тип класса, кроме нестатического внутреннего класса.
В приведенном выше фрагменте кода оба класса имеют свой отдельный исходный файл. Однако мы также можем использовать статический внутренний класс:
Обратите внимание, что Java создаст неявную ссылку только в нестатических внутренних классах. Из-за этого мы должны поддерживать отношения вручную там, где нам это нужно:
4. Ассоциация
Ассоциация означает только то, что объекты “знают” друг друга. Например, мать и ее ребенок.
4.1. UML
В UML мы можем отметить ассоциацию стрелкой:
Если связь двунаправленная, мы можем использовать две стрелки, стрелку с наконечником на обоих концах или линию без наконечников:
Мы можем представить мать и ее ребенка в UML, тогда:
4.2. Исходный код
В Java мы можем моделировать ассоциацию так же, как и агрегацию:
Но подождите, как мы можем определить, означает ли ссылка агрегацию или ассоциацию?
Ну, мы не можем. Разница только логична: является ли один из объектов частью другого или нет.
Кроме того, мы должны поддерживать ссылки вручную на обоих концах, как мы делали с агрегацией:
5. UML Sidenote
Для ясности иногда мы хотим определить мощность отношения на диаграмме UML. Мы можем сделать это, записав его на концах стрелки:
Обратите внимание, что нет смысла писать ноль в качестве мощности, потому что это означает, что нет никакой связи. Единственное исключение-это когда мы хотим использовать диапазон для указания необязательной связи:
Также обратите внимание, что, поскольку в составе есть только один владелец, мы не указываем его на диаграммах.
6. Сложный Пример
Давайте рассмотрим (немного) более сложный пример!
Мы смоделируем университет, в котором есть свои кафедры. На каждой кафедре работают профессора, у которых также есть друзья друг среди друга.
Будут ли кафедры существовать после того, как мы закроем университет? Конечно, нет, поэтому это композиция.
Но профессора все равно будут существовать (надеюсь). Мы должны решить, что более логично: считать ли профессоров частью кафедр или нет. Альтернативно: являются ли они членами департаментов или нет? Да, это так. Следовательно, это агрегация. Кроме того, профессор может работать на нескольких кафедрах.
Отношения между профессорами являются ассоциативными, потому что нет никакого смысла говорить, что профессор является частью другого.
В результате мы можем смоделировать этот пример со следующей диаграммой UML:
И код Java выглядит следующим образом:
7. Заключение
В этой статье мы рассмотрели свойства и представление композиции, агрегации и ассоциации. Мы также видели, как моделировать эти отношения в UML и Java.
Что такое Композиция? Пример Композиции в Java
Композиция является одним из методов проектирования, который реализовывает отношение типа has-a в классах. Мы можем использовать наследование в Java или композицию для повторного использования кода.
Композиция в Java достигается за счет использования переменных экземпляра, который ссылается на другие объекты.
Небольшой пример в теории: Person has a (имеет) Job.
А теперь практика на java:
А теперь протестируем: используем объект Person и получаем зарплату (salary)
Обратите внимание, что тестовая программа TestPerson не зависит от каких-либо изменений в объекте Job. Если вам нужно реализовать взаимодействие двух классов и вы хотите повторно использовать код, то лучшим выходом будет использование композиции вместо наследования.
Преимущества использования композиции в том, что мы можем управлять видимостью другого объекта для клиентских классов и повторно использовать только то, что нам нужно.
Кроме того, если есть какие-либо изменения в другой реализации класса, например, если метод getSalary() начнет возвращать строку, то мы должны изменить класс Person, а не классы клиента.
Композиция в Java позволяет создавать back-end класс, когда это необходимо. Например, мы можем изменить getSalary() метод класса Person для инициализации объекта Job.
Больше полезных статей!
3 thoughts to “Что такое Композиция? Пример Композиции в Java”
Здравствуйте. Объясните пожалуйста следующий момент.
Вот определение Композиции которое представлено в Википедии:
Композиция — более строгий вариант агрегации. Известна также как агрегация по значению.
Композиция имеет жёсткую зависимость времени существования экземпляров класса контейнера и экземпляров содержащихся классов. Если контейнер будет уничтожен, то всё его содержимое будет также уничтожено.
В принципе это созвучно с тем, как излагают данный вопрос Джим Арлоу и Айла Нейштадт в книге UML2 :
Композиция — это строгая форма агрегации:
1) Одновременно части могут принадлежать только одному композиту — совместное владение частями невозможно.
2) композит обладает исключительной ответственностью за все свои части; это значит что он отвечает за их создание и уничтожение
3) композит может высвобождать части, передавая ответственность за них другому объекту
4) в случае уничтожения композита он должен уничтожить все свои части или передать ответственность за них другому объекту.
Вопрос в следующем: данный пример показывает свойства композиции или все таки агрегации, Ведь фактически пример не отвечает ни одному вышеперечисленному пункту. Или я что — то не так понимаю. Заранее благодарен.
Наследование, композиция, агрегация
Нередко случается, что решив разобраться с какой-то новой темой, понятием, инструментом программирования, я читаю одну за другой статьи на различных сайтах в интернете. И, если тема сложная, то эти статьи могут не на шаг не приблизить меня к понимаю. И вдруг встречается статья, которая моментально дает озарение и все паззлы складываются воедино. Трудно определить, что отличает такую статью от других. Правильно подобранные слова, оптимальная логика изложения или же просто более релевантный пример. Я не претендую на то, что моя статься окажется новым словом в C# или же лучшей обучающей статьей. Но, возможно для кого-то она станет именно той, которая позволит разобраться, запомнить и начать правильно применять те понятия, о которых пойдет речь.
В объектно-ориентированных языках программирования существует три способа организации взаимодействия между классами. Наследование — это когда класс-наследник имеет все поля и методы родительского класса, и, как правило, добавляет какой-то новый функционал или/и поля. Наследование описывается словом «является». Легковой автомобиль является автомобилем. Вполне естественно, если он будет его наследником.
Ассоциация – это когда один класс включает в себя другой класс в качестве одного из полей. Ассоциация описывается словом «имеет». Автомобиль имеет двигатель. Вполне естественно, что он не будет являться наследником двигателя (хотя такая архитектура тоже возможна в некоторых ситуациях).
Выделяют два частных случая ассоциации: композицию и агрегацию.
Композиция – это когда двигатель не существует отдельно от автомобиля. Он создается при создании автомобиля и полностью управляется автомобилем. В типичном примере, экземпляр двигателя будет создаваться в конструкторе автомобиля.
Агрегация – это когда экземпляр двигателя создается где-то в другом месте кода, и передается в конструктор автомобиля в качестве параметра.
Хотя ведутся дискуссии о преимуществах того или иного способа организации взаимодействия между классами, какого-либо абстрактного правила не существует. Разработчик выбирает тот или иной путь основываясь на элементарной логике (“является” или “имеет”), но также принимает во внимание возможности и ограничения, которые дают и накладывают эти способы. Для того, чтобы увидеть эти возможности и ограничения, я попытался написать пример. Достаточно простой, чтобы код оставался компактным, но и достаточно развитый, чтобы в рамках одной программы можно было применить все три способа. И, главное, я попытался сделать этот пример как можно менее абстрактным – все объекты и экземпляры понятны и осязаемы.
Напишем простенькую игру – танковый бой. Играют два танка. Они поочередно стреляют и проигрывает тот, здоровье которого упало до нуля. В игре будут различные типы снарядов и брони. Для того, чтобы нанести урон необходимо во-первых, попасть по танку противника, во-вторых, пробить его броню. Если броня не пробита, урон не наносится. Логика игры построена на принципе «камень-ножницы-бумага»: то есть броня одного типа хорошо противостоит снарядам определенного типа, но плохо держит другие снаряды. Кроме того, снаряды, которые хорошо пробивают броню, наносят малый «заброневой» урон, и, напротив, наиболее «летальные» снаряды имеют меньше шансов пробить броню.
Создадим простенький класс для пушки. Он будет иметь два приватных поля: калибр и длину ствола. От калибра зависит урон, и, частично, способность к пробитию брони. От длины ствола – точность стрельбы.
Сделаем также конструктор для пушки:
Сделаем метод для получения калибра из других классов:
Помните, что для поражения цели должно произойти две вещи: попадание в цель и пробитие брони? Так вот, пушка будет отвечать за первую из них: попадание. Поэтому делаем булевый метод IsOnTarget, который принимает случайную величину (dice) и возвращает результат: попали или нет:
Целиком класс пушки выглядит следующим образом:
Здесь мы применили агрегацию. Где-то будет создана пушка. Потом к этой пушке будут создаваться снаряды, которые имеют указатель на пушку.
Теперь сделаем разные типы снарядов, которые будут наследовать абстрактный снаряд: фугасный, кумулятивный, подкалиберный. Фугасный наносит самый большой урон, кумулятивный – меньше, подкалиберный – еще меньше. Дочерние классы не имеют полей и вызывают конструктор базового снаряда, передавая ему пушку, и строковый тип. В дочернем классе переопределяется метод GetDamage() – вносятся коэффициенты, которые увеличат или уменьшат урон по сравнению с дефолтным.
Фугасный (дефолтный урон):
Кумулятивный (дефолтный урон х 0.6):
Подкалиберный (дефолтный урон х 0.3):
Обратите внимание, что в переопределенном методе GetDamage вызывается и метод базового класса. То есть, переопределив метод, мы также сохраняем возможность обратиться к дефолтному методу, использовав ключевое слово base).
Итак, для снарядов мы применили и агрегацию (пушка в базовом классе), и наследование.
Создадим теперь броню для танка. Здесь применим только наследование. Любая броня имеет толщину. Поэтому абстрактный класс брони будет иметь поле thickness, и строковое поле type, которое будет определятся при создании дочерних классов.
Броня будет в нашей игре определять пробита они или нет. Поэтому, у нее будет лишь один метод, который будет переопределяться в дочерних, в зависимости от типа брони.
Для того, чтобы конструктор танка остался более-менее компактным, сделаем два вспомогательных приватных метода, которые добавляют три типа брони соответствующей толщины, и наполняют боеукладку 10 снарядами каждого из трех типов:
Теперь конструктор танка выглядит вот таким образом:
Пользовательский интерфейс танка состоит из трех методов: выбрать броню, зарядить пушку, выстрелить.
Как я упомянул в начале, в этом примере я старался максимально уйти от абстрактных понятий, которые нужно все время держать в голове. Поэтому каждый экземпляр снаряда у нас равен физическому снаряду, который положили в боеукладку перед боем. Следовательно, снаряды могут закончится в самый неподходящий момент!
Этот интерфейс требует реализации метода Clone(). Вот она:
Теперь все супер реалистично: при выстреле генерируется dice, пушка рассчитывает попадание своим методом IsOnTarget, и, если попадание есть, то метод Shoot вернет экземпляр снаряда, а если промах – то вернет null.
Последний метод танка – его поведение при попадании вражеского снаряда:
Все готово. Остается только написать консольный (или неконсольный) вывод, в котором будет обеспечен пользовательский интерфейс и в цикле реализованы поочередные ходы игроков.
Подведем итоги. Мы написали программу, в которой использовали наследование, композицию и агрегацию, надеюсь, поняли и запомнили различия. Активно задействовали возможности полиморфизма, во-первых, когда любые экземпляры дочерних классов можно сложить в список, имеющий тип данных родительского, а во-вторых, создавая методы, которые принимают в качестве параметра родительский экземпляр, но внутри которых вызываются методы дочернего. По ходу текста я упоминал возможные альтернативные реализации – замену наследования на агрегацию, и, универсального рецепта тут нет. В нашей реализации наследование дало нам легкость добавления новых деталей в игру. Например, чтобы добавить новый тип снаряда нам нужно лишь:
Ниже – приведена диаграмма наших классов.
В финальном коде игры все «магические числа», которые использовались в тексте, вынесены в отдельный статический класс Config. К публичным полям статического класса мы можем обратиться из любого фрагмента нашего кода и его экземпляр не нужно (и невозможно) создавать. Вот так он выглядит: