Страницы

Поиск по вопросам

вторник, 26 ноября 2019 г.

Агрегация и композиция


Здравствуйте!

Всегда считал, что агрегация — это синоним композиции, однако наткнулся на блог в интернете, где приводятся отличия композиции от агрегации.

Мне это снесло крышу. Поясните, пожалуйста, плюсы/минусы и того и другого на небольших примерах. Как это влияет на расширяемость, тестируемость и т. д.
    


Ответы

Ответ 1



Существует несколько видов взаимодействия объектов, объединенных под общим понятие "Has-A Relationship" или "Part Of Relationship". Это отношение означает, что один объект является составной частью другого объекта. Существует два подвида этого отношения: если один объект создает другой объект время жизни "части" зависит от времени жизни целого, то это называется "композиция", если же один объект получает ссылку (указатель) на другой объект в процессе конструирования, то это уже агрегация. Давайте рассмотрим пример из .NET Framework, чтобы увидеть, какие ограничения/последствия несут эти отношения: StringWriter + StringBuilder. Класс StringWriter - это специализированная версия класса TextWriter, которые активно используются при сериализации и для получения текстового представления объектов. Конкретно StringWriter создает строковое представление объекта или графа объекто и опирается на экземпляр StringBuilder в своей работе. Т.е. мы можем сказать, что 'StringWrite HAS A StringBuilder' или 'StringBuilder is part of StringWriter'. Теперь давайте посмотрим, как решить, должен ли StringWriter получать экземпляр StringBuilder-а извне или создавать его? С одной стороны, нам, как клиенту класса StringWriter часто бывает все равно, чт именно используется внутри этого класса для получения строкового представления. Это значит, что с точки зрения простоты использования лучше, чтобы StringWriter создавал экземпляр StringBuilder-а самостоятельно. Но, с другой стороны, конкретный объект StringWriter-а может отвечать лишь за получени части строкового представления, а другая часть строки может вычисляться другим способом С этой точки зрения, лучше, чтобы StringWriter принимал экземлпяр StringBuilder-а в конструкторе. Это же справедливо и для высоконагруженных систем, в которых разумно использование пула объектов. Поскольку StringWriter - это библиотечный класс, который должен поддерживать об сценария, то у него есть перегруженные версии конструктора: один из них создает StringBuilder внутри, а другой - принимает его снаружи. Другими словами, выбор между композицией и агрегацией основан на соблюдении балланса между различными требованиями в дизайне: Композиция: объект A управляет временем жизни объекта B Плюсы: Композиция позволяет скрыть отношение использования объектов от глаз клиента. Делает API использования класса более простым и позволяет перейти от использовани одного класса, к другому (например, StringWriter мог бы поменять реализацию и начать использовать другой тип, например CustomStringBuilder). Минусы: Отношение достаточно жесткое, поскольку один объект должен уметь создавать другой он должен знать конкретный тип и иметь доступ к функции создания. Композиция не позволяе использовать интерфейсы (без привлечения фабрик) и требует, чтобы класс имел доступ к конструктору другого класса: представьте, что конструктор StringBuilder-а является внутренним или же это интерфейс IStringBuilder и только клиенский код знает, какой экземпляр должен использоваться здесь и сейчас. Агрегация: объект А получает ссылку на объект B Плюсы: Более слабая связанность между объектом и его клиентом. Теперь мы можем использовать интерфейсы и одному объекту не нужно знать, как именно создавать другой объект. Большая гибкость. Вытекает из первого пункта Минусы: Выставление наружу деталей реализации. Поскольку клиент класса должен предоставит зависимость в момент создания объекта (передать экземпляр StringBuilder-а в момент создания StringWriter-а, то сам факт этого отношения становится известен клиенту. Из первого пункта вытекает увеличение сложности в работе клиентов, а также больша "жесткость" решения в долгосрочной перспективе. Теперь автор класса TextWriter уже н может принять решение самостоятельно и перейти от StringBuilder-а к чему-то другому. Да, можно "добавить еще один уровень абстракции" и выделить интерфейс IStringBuilder, но разорвать это отношение совсем будет невозможно без поломки всех существующих клиентов. В заключении: дизайн - это поиск компромисса между различными факторами. Композици проще с точки зрения клиентов класса, но налагает определенные ограничения: "целое должно уметь создавать "составную часть". Агрегация гибче, но налагает другие ограничения: теперь "целое" не скрывает о существовании "составной части", а значит и не сможет заменить ее на другую "составную часть" в будущем. P.S. Если очень хочется использовать пример из реального мира, то для объяснени композиции и агрегации может подойти ... отвертка. Если отвертка цельная, т.е. ручк и насадка намертво связаны друг с другом, то мы имеем отношение композиции. Если же насадка съемная и может существовать без ручки или же использоваться с другой ручкой, то мы имеем отношение агрегации.

Комментариев нет:

Отправить комментарий