Существует классическая проблема ООП цепочки методов по сравнению с методами с одной точкой доступа:
main.getA().getB().getC().transmogrify(x, y)
против
main.getA().transmogrifyMyC(x, y)
Первый, кажется, имеет то преимущество, что каждый класс отвечает только за меньший набор операций и делает все намного более модульным - добавление метода к C не требует никаких усилий в A, B или C для его раскрытия.
Недостатком, конечно, является более слабая инкапсуляция , которую решает второй код. Теперь A имеет контроль над каждым методом, который проходит через него, и может делегировать его своим полям, если он этого хочет.
Я понимаю, что не существует единого решения, и оно, конечно, зависит от контекста, но мне бы очень хотелось услышать некоторую информацию о других важных различиях между этими двумя стилями и при каких обстоятельствах я бы предпочел один из них - потому что прямо сейчас, когда я пытаюсь чтобы создать какой-то код, я чувствую, что просто не использую аргументы, чтобы решить так или иначе.
Я вообще стараюсь, чтобы цепочка методов была как можно более ограниченной (на основе закона Деметры )
Единственное исключение, которое я делаю, - это свободное владение интерфейсами / программирование в стиле DSL.
Мартин Фаулер проводит такое же различие в предметно-ориентированных языках, но по причинам нарушения разделения командных запросов, которое гласит:
Фаулер в своей книге на странице 70 говорит:
источник
Я думаю, вопрос в том, используете ли вы подходящую абстракцию.
В первом случае мы имеем
Где основной тип
IHasGetA
. Вопрос в том, подходит ли эта абстракция. Ответ не тривиален. И в этом случае это выглядит немного не так, но в любом случае это теоретический пример. Но для построения другого примера:Часто лучше чем
Потому что в последнем примере , как
this
и вmain
зависимости от типовv
,w
,x
,y
иz
. Кроме того, код не выглядит намного лучше, если каждое объявление метода имеет полдюжины аргументов.Сервисный локатор на самом деле требует первого подхода. Вы не хотите получать доступ к экземпляру, который он создает, через локатор службы.
Таким образом, «прохождение» через объект может создать большую зависимость, тем более, если оно основано на свойствах реальных классов.
Однако создание абстракции, которая заключается в предоставлении объекта, - это совсем другое.
Например, вы могли бы иметь:
Где
main
это экземплярMain
. Если знание классаmain
уменьшает зависимостьIHasGetA
вместоMain
, вы обнаружите, что связь на самом деле довольно низкая. Вызывающий код даже не знает, что он на самом деле вызывает последний метод исходного объекта, что фактически иллюстрирует степень развязки.Вы идете по пути кратких и ортогональных абстракций, а не глубоко во внутреннюю часть реализаций.
источник
Закон Деметры , а @ Петера Török указывает, предлагает «компактную» форму.
Кроме того, чем больше методов вы явно упоминаете в своем коде, тем больше классов зависит от вашего класса, что увеличивает проблемы обслуживания. В вашем примере компактная форма зависит от двух классов, тогда как более длинная форма зависит от четырех классов. Более длинная форма не только противоречит Закону Деметры; это также заставит вас изменять ваш код каждый раз, когда вы меняете любой из четырех упомянутых методов (в отличие от двух в компактной форме).
источник
A
взорвется, и многие методы,A
возможно, захотят все равно отменить. Тем не менее, я согласен с зависимостями - это значительно уменьшает количество зависимостей, требуемых от клиентского кода.Я боролся с этой проблемой сам. Недостатком «глубокого проникновения» в разные объекты является то, что при рефакторинге вам в конечном итоге придется менять очень много кода, поскольку существует так много зависимостей. Кроме того, ваш код становится немного раздутым и трудным для чтения.
С другой стороны, наличие классов, которые просто «передают» методы, также подразумевает необходимость объявлять несколько методов в более чем одном месте.
Решение, которое смягчает это и уместно в некоторых случаях, заключается в наличии фабричного класса, который создает своего рода объект фасада путем копирования данных / объектов из соответствующих классов. Таким образом, вы можете кодировать свой фасадный объект, а при рефакторинге вы просто меняете логику фабрики.
источник
Я часто нахожу, что логику программы легче понять цепными методами. Для меня это
customer.getLastInvoice().itemCount()
вписывается в мой мозг лучше, чемcustomer.countLastInvoiceItems()
.Стоит ли это головной боли от обслуживания дополнительного соединения, решать только вам. (Мне также нравятся маленькие функции в маленьких классах, поэтому я склонен цепляться. Я не говорю, что это правильно - это именно то, что я делаю.)
источник