Я пытаюсь изучить GRASP, и я нашел это объяснение ( здесь на странице 3 ) о низком соединении, и я был очень удивлен, когда обнаружил следующее:
Рассмотрим метод
addTrack
дляAlbum
класса, два возможных метода:
addTrack( Track t )
и
addTrack( int no, String title, double duration )
Какой метод уменьшает сцепление? Второй делает, так как класс, использующий класс Album, не должен знать класс Track. В общем случае параметры методов должны использовать базовые типы (int, char ...) и классы из пакетов java. *.
Я склонен не соглашаться с этим; Я считаю, addTrack(Track t)
что лучше, чем addTrack(int no, String title, double duration)
по разным причинам:
Для метода всегда лучше использовать как можно меньше параметров (согласно «Чистому коду» дяди Боба ни один или один предпочтительно, 2 в некоторых случаях и 3 в особых случаях; более 3 требуют рефакторинга - это, конечно, рекомендации, а не правила Холли) ,
Если
addTrack
это метод интерфейса, а требованияTrack
должны иметь больше информации (скажем, год или жанр), то интерфейс необходимо изменить, чтобы метод поддерживал другой параметр.Инкапсуляция нарушена; если
addTrack
в интерфейсе, то он не должен знать внутренностиTrack
.Это на самом деле более связано вторым способом, со многими параметрами. Предположим, что
no
параметр необходимо изменить сint
на,long
потому что есть больше, чемMAX_INT
дорожки (или по любой причине); тогдаTrack
нужно изменить и метод, и метод, в то время как если бы метод был измененaddTrack(Track track)
только наTrack
.
Все 4 аргумента на самом деле связаны друг с другом, и некоторые из них являются следствием других.
Какой подход лучше?
Ответы:
Ну, ваши первые три пункта на самом деле о других принципах, чем сцепление. Вы всегда должны найти баланс между противоречивыми принципами дизайна.
Ваша четвертая точка находится около сцепления, и я полностью согласен с вами. Связь - это поток данных между модулями. Тип контейнера, в который поступают данные, в значительной степени несущественен. Передача длительности в виде двойного числа вместо поля
Track
не устраняет необходимость его пропускать. Модули по-прежнему должны совместно использовать один и тот же объем данных и иметь одинаковое количество связей.Он также не рассматривает все связи в системе как совокупность. Хотя введение
Track
класса по общему признанию добавляет еще одну зависимость между двумя отдельными модулями, это может значительно уменьшить сцепление системы , что является здесь важной мерой.Например, рассмотрим кнопку «Добавить в плейлист» и
Playlist
объект. ПредставлениеTrack
объекта можно было бы рассмотреть для увеличения связи, если вы рассматриваете только эти два объекта. Теперь у вас есть три взаимозависимых класса вместо двух. Однако это не вся ваша система. Вам также необходимо импортировать дорожку, воспроизвести дорожку, отобразить дорожку и т. Д. Добавление еще одного класса к этому миксу незначительно.Теперь рассмотрите необходимость добавить поддержку воспроизведения треков по сети, а не только локально. Вам просто нужно создать
NetworkTrack
объект, который соответствует тому же интерфейсу. БезTrack
объекта вам пришлось бы создавать функции везде, например:Это фактически удваивает вашу связь, требуя, чтобы даже модули, которые не заботятся о специфических для сети вещах, все же отслеживали ее, чтобы иметь возможность передавать ее.
Ваш тест на волновой эффект является хорошим для определения вашего истинного количества сцепления. Что нас беспокоит, так это ограничение мест, на которые влияет изменение.
источник
Моя рекомендация:
использование
но убедитесь, что
ITrack
это интерфейс, а не конкретный класс.Альбом не знает внутренностей
ITrack
разработчиков. Это только связано с договором, определеннымITrack
.Я думаю, что это решение, которое генерирует наименьшее количество связей.
источник
Track
того, глупы ли вы или умны.Track
это конкреция.ITrack
Интерфейс это абстракция. Таким образом, вы сможете иметь различные типы треков в будущем, если они соответствуютITrack
.Я бы сказал, что второй пример метода, скорее всего, увеличивает сцепление, поскольку он, скорее всего, создает экземпляр объекта Track и сохраняет его в текущем объекте Album. (Как было предложено в моем комментарии выше, я бы предположил, что классу Album будет присуще понятие класса Track где-то внутри него.)
В первом примере метода предполагается, что экземпляр Track создается вне класса Album, поэтому, по крайней мере, мы можем предположить, что создание экземпляра класса Track не связано с классом Album.
Если бы лучшие практики предполагали, что у нас никогда не будет ссылки на один класс во втором классе, вся объектно-ориентированное программирование будет выброшено в окно.
источник
Соединение - это лишь один из многих аспектов, которые нужно получить в своем коде. Уменьшая сцепление, вы не обязательно улучшаете свою программу. В общем, это лучшая практика, но в данном конкретном случае почему не
Track
следует знать?Используя
Track
класс для передачиAlbum
, вы облегчаете чтение кода, но, что более важно, как вы уже упоминали, вы превращаете статический список параметров в динамический объект. Это в конечном итоге делает ваш интерфейс гораздо более динамичным.Вы упоминаете, что инкапсуляция нарушена, но это не так.
Album
должен знать внутренностиTrack
, и, если вы не использовали объект,Album
должен был бы знать каждую часть информации, передаваемую ему, прежде чем он мог бы все же использовать его. Вызывающая сторона также должна знать внутренние компонентыTrack
, поскольку она должна создаватьTrack
объект, но вызывающая сторона должна знать эту информацию все равно, если она была передана непосредственно методу. Другими словами, если преимущество инкапсуляции заключается в том, что она не знает содержимого объекта, его нельзя было бы использовать в этом случае, так как онAlbum
должен использоватьTrack
информацию s точно так же.То, что вы не хотели бы использовать,
Track
- этоTrack
содержит внутреннюю логику, к которой вы не хотите, чтобы вызывающий имел доступ. Другими словами, если быAlbum
был класс, который должен был использовать программист, использующий вашу библиотеку, вы бы не хотели, чтобы он использовал его,Track
если вы используете его, скажем, вызов метода для сохранения его в базе данных. Истинная проблема с этим заключается в том, что интерфейс запутан с моделью.Чтобы решить эту проблему, вам нужно разделить
Track
компоненты интерфейса и логические компоненты, создав два отдельных класса. Для вызывающего абонентаTrack
становится легким классом, который предназначен для хранения информации и предлагает незначительные оптимизации (вычисленные данные и / или значения по умолчанию). ВнутриAlbum
вы будете использовать класс с именемTrackDAO
для выполнения тяжелой работы, связанной с сохранением информацииTrack
в базе данных.Конечно, это всего лишь пример. Я уверен, что это не ваш случай, и поэтому не стесняйтесь использовать без
Track
вины. Просто не забывайте помнить своего вызывающего при создании классов и создавать интерфейсы при необходимости.источник
Оба верны
это лучше (как вы уже аргументировано) , а
будет меньше связаны , так как код , который использует
addTrack
не нужно знать , что естьTrack
класс. Трек может быть переименован, например, без необходимости обновления вызывающего кода.В то время как вы говорите о более читабельном / поддерживаемом коде, в статье говорится о соединении . Менее связанный код не обязательно легче реализовать и понять.
источник
Низкая связь не означает отсутствие связи. Что-то где-то должно знать об объектах в другом месте кодовой базы, и чем больше вы уменьшаете зависимость от «пользовательских» объектов, тем больше причин вы приводите к изменению кода. То, что автор, которого вы цитируете, продвигает со второй функцией, является менее связанным, но также менее объектно-ориентированным, что противоречит всей идее GRASP как методологии объектно-ориентированного проектирования. Все дело в том, как спроектировать систему как совокупность объектов и их взаимодействия; избегать их - все равно что учить вас водить машину, говоря, что вместо этого вы должны ездить на велосипеде.
Вместо этого, правильный путь - уменьшить зависимость от конкретных объектов, что является теорией «слабой связи». Чем меньше конкретных конкретных типов, о которых должен знать метод, тем лучше. Именно этим утверждением первый вариант на самом деле менее связан, поскольку второй метод, использующий более простые типы, должен знать обо всех этих более простых типах. Конечно, они встроены, и код внутри метода, возможно, должен заботиться, но сигнатура метода и вызывающие его методы определенно этого не делают . Изменение одного из этих параметров, относящихся к концептуальной звуковой дорожке, потребует большего количества изменений, когда они разделены по сравнению с тем, когда они содержатся в объекте дорожки (который является точкой объектов; инкапсуляция).
Пройдя еще один шаг вперед, если предполагалось, что Track будет заменен чем-то, что делает ту же работу лучше, возможно, будет необходим интерфейс, определяющий необходимые функции, ITrack. Это может позволить различные реализации, такие как «AnalogTrack», «CdTrack» и «Mp3Track», которые предоставляют дополнительную информацию, более специфичную для этих форматов, и в то же время обеспечивают базовое представление данных ITrack, которое концептуально представляет «дорожку»; конечный фрагмент аудио. Track также может быть абстрактным базовым классом, но для этого необходимо, чтобы вы всегда хотели использовать реализацию, присущую Track; переопределите его как BetterTrack, и теперь вы должны изменить ожидаемые параметры.
Таким образом, золотое правило; Программы и их компоненты кода всегда будут иметь причины для изменения. Вы не можете написать программу, которая никогда не потребует редактирования кода, который вы уже написали, чтобы добавить что-то новое или изменить его поведение. Ваша цель в любой методологии (GRASP, SOLID, любой другой акроним или модное слово, которое вы можете придумать) состоит в том, чтобы просто определить вещи, которые должны будут измениться со временем, и спроектировать систему так, чтобы эти изменения были как можно проще сделать (переведено; затрагивает как можно меньше строк кода и затрагивает как можно меньше других областей системы, выходящих за рамки предполагаемого изменения). В данном случае, скорее всего, изменится то, что дорожка получит больше элементов данных, которые addTrack () может интересовать, а может и не заботиться, а не этот трек будет заменен на BetterTrack.
источник