Я не ищу мнение о семантике, а просто для случая, когда разумно использовать геттеры - это реальное препятствие. Может быть, это бросает меня в бесконечную спираль доверия к ним, может быть, альтернатива чище и автоматически обрабатывает добытчики, и т.д. Что-то конкретное.
Я слышал все аргументы, я слышал, что они плохие, потому что они заставляют вас рассматривать объекты как источники данных, что они нарушают «чистое состояние» объекта «не выдавайте слишком много, но будьте готовы принять много ".
Но абсолютно нет разумной причины, почему это getData
плохо, на самом деле, несколько человек утверждали, что это много о семантике, о добытчиках как таковых, но просто не называйте их getX
, для меня, это по крайней мере забавно ,
Что, без мнений, может сломаться, если я разумно использую геттеры и для данных, которые, безусловно, не нарушают целостность объекта, если он его выпускает?
Конечно, использование геттера для строки, которая используется для шифрования чего-либо, выходит за рамки глупости, но я говорю о данных, которые необходимы для работы вашей системы. Возможно, ваши данные извлекаются через объект Provider
из объекта, но, тем не менее, объекту все еще нужно разрешить, Provider
чтобы сделать это $provider[$object]->getData
, нет никакого способа обойти это.
Почему я спрашиваю: для меня геттеры, когда они используются разумно и на данных, которые рассматриваются как «безопасные», отправляются богом, 99% моих геттеров используются для идентификации объекта, как, я спрашиваю, с помощью кода Object, what is your name? Object, what is your identifier?
, любой, кто работает с объектом, должен знать эти вещи об объекте, потому что почти все в программировании - это личность, а кто еще знает, что это такое, чем сам объект? Поэтому я не вижу никаких реальных проблем, если вы не пурист.
Я рассмотрел все вопросы StackOverflow о том, «почему геттеры / сеттеры» плохи, и хотя я согласен, что сеттеры действительно плохи в 99% случаев, геттеры не должны рассматриваться одинаково только потому, что они рифмуются.
Установщик скомпрометирует идентичность вашего объекта и сделает очень трудным отладку, кто изменяет данные, но получатель ничего не делает.
источник
Ответы:
Вы не можете написать хороший код без получателей.
Причина не в том, что геттеры не нарушают инкапсуляцию, а делают. Это не потому, что геттеры не соблазняют людей не беспокоиться об ООП, что заставило бы их размещать методы с данными, на которые они воздействуют. Они делают. Нет, вам нужны добытчики из-за границ.
Идеи инкапсуляции и хранения методов вместе с данными, с которыми они работают, просто не работают, когда вы сталкиваетесь с границей, которая удерживает вас от перемещения метода и, таким образом, вынуждает перемещать данные.
Это действительно так просто. Если вы используете геттеры, когда нет границ, у вас не будет реальных объектов. Все начинает стремиться к процессуальному. Который работает так же хорошо, как и когда-либо.
Настоящий ООП - это не то, что вы можете распространять повсюду Это работает только в этих границах.
Эти границы не тонкие как бритва. У них есть код в них. Этот код не может быть ООП. Это также не может быть функциональным. Нет, этот код не лишил нас наших идеалов, чтобы он мог справиться с суровой реальностью.
Майкл Феттерс назвал этот код фасции в честь этой белой соединительной ткани, которая скрепляет участки апельсина.
Это прекрасный способ думать об этом. Это объясняет, почему нормально иметь оба вида кода в одной кодовой базе. Без этой перспективы многие новые программисты сильно цепляются за свои идеалы, а затем разбивают им сердца и отказываются от этих идеалов, когда достигают своей первой границы.
Идеалы работают только на своем месте. Не отказывайтесь от них только потому, что они не работают везде. Используйте их там, где они работают. Это самое сочное место, которое защищает фасция.
Простой пример границы - это коллекция. Это что-то держит и понятия не имеет, что это такое. Как дизайнер коллекции мог бы перемещать поведенческую функциональность удерживаемого объекта в коллекцию, когда он не знает, что он будет держать? Ты не можешь Ты против границы. Именно поэтому в коллекциях есть геттеры.
Теперь, если бы вы знали, вы могли бы изменить это поведение и избежать изменения состояния. Когда вы знаете, вы должны. Ты просто не всегда знаешь.
Некоторые люди просто называют это прагматичным. И это. Но приятно знать, почему мы должны быть прагматичными.
Вы заявили, что не хотите слышать семантические аргументы и, похоже, отстаиваете повсеместное использование «разумных добытчиков». Вы просите, чтобы эта идея была оспорена. Я думаю, что могу показать, что у идеи есть проблемы с тем, как вы ее сформулировали. Но мне кажется, я знаю, откуда ты, потому что я был там.
Если вы хотите, чтобы получатели везде смотрели на Python. Частного ключевого слова нет. Тем не менее, Python прекрасно работает. Как? Они используют смысловой трюк. Они называют все, что должно быть частным, с ведущим подчеркиванием. Вам даже разрешают читать, если вы берете на себя ответственность за это. «Мы все здесь взрослые», - часто говорят они.
Так в чем же разница и просто помещать геттеры на все в Java или C #? Извините, но это семантика. Согласие Питона подчеркивает, что вы ясно видите, что вы ковыряетесь за дверью сотрудников. Хлопайте добытчики на все, и вы теряете этот сигнал. С отражением вы могли бы в любом случае лишиться частного и все же не потерять смысловой сигнал. Здесь просто нет структурного аргумента.
Так что нам остается решить, куда повесить табличку «только для сотрудников». Что следует считать приватным? Вы называете это "разумными добытчиками". Как я уже сказал, лучшее оправдание для добытчика - это граница, которая отталкивает нас от наших идеалов. Это не должно привести к получению на все. Когда это приводит к получению, вы должны подумать о том, чтобы продвинуть поведение дальше в область, где вы можете его защитить.
Это разделение породило несколько условий. Объект передачи данных или DTO не содержит поведения. Единственными методами являются методы получения, а иногда и установки, иногда конструктора. Это имя неудачное, потому что это совсем не настоящий объект. Получатели и установщики на самом деле являются просто отладочным кодом, который дает вам возможность установить точку останова. Если бы не было этой необходимости, они были бы просто кучей публичных полей. В C ++ мы называли их структурами. Единственное отличие, которое они имели от класса C ++, заключалось в том, что они по умолчанию были общедоступными.
DTO хороши тем, что вы можете перебросить их через граничную стену и сохранить другие ваши методы безопасно в приятном сочном объекте поведения. Настоящий объект. Без получателей, чтобы нарушить его инкапсуляцию. Мои объекты поведения могут есть DTO, используя их в качестве объектов параметров . Иногда мне приходится делать защитную копию, чтобы предотвратить общее изменяемое состояние . Я не распространяю изменяемые DTO внутри сочной части внутри границы. Я заключаю их в капсулу. Я их скрываю И когда я, наконец, сталкиваюсь с новой границей, я раскручиваю новый DTO и бросаю его через стену, создавая, таким образом, чужую проблему.
Но вы хотите предоставить получателям, которые выражают личность. Ну поздравляю, вы нашли границу. У сущностей есть идентичность, которая выходит за рамки их ссылок. То есть за пределами их памяти. Так что это должно где-то храниться. И что-то должно иметь возможность ссылаться на эту вещь по своей идентичности. Получатель, который выражает идентичность, совершенно разумен. Куча кода, который использует этот геттер для принятия решений, которые сущность могла бы принять сама, не является таковой.
В конце концов, это не существование добытчиков, что неправильно. Они намного лучше, чем публичные поля. Что плохо, когда они используются, чтобы притворяться, что вы объектно-ориентированы, а вы нет. Геттерс это хорошо. Быть объектно-ориентированным - это хорошо. Получатели не являются объектно-ориентированными. Используйте добытчики, чтобы вырезать безопасное место, чтобы быть объектно-ориентированным.
источник
clearly
? Если данные передаются мне от чего-то, по значению , ясно, что мой объект теперь владеет данными, но по семантике он еще не, на данный момент он просто получил данные от кого-то другого. Затем он должен выполнить операции для преобразования этих данных, сделав их своими собственными, в момент касания данных вы должны внести семантические изменения: у вас есть данные, названные как,$data_from_request
и теперь вы оперировали с ними? Назовите это$changed_data
. Вы хотите раскрыть эти данные другим? Создайте геттерgetChangedRequestData
, тогда право собственности четко установлено. Или это?Сборщики нарушают Голливудский принцип («Не звоните нам, мы вам позвоним»)
Голливудский принцип (он же Inversion of Control) гласит, что вы не вызываете библиотечный код для достижения цели; скорее, фреймворк вызывает ваш код. Поскольку среда управляет вещами, передача своего внутреннего состояния своим клиентам не требуется. Вам не нужно знать.
В своей самой коварной форме нарушение Голливудского принципа означает, что вы используете метод получения для получения информации о состоянии класса, а затем принимаете решение о том, какие методы вызывать для этого класса, основываясь на полученном вами значении. это нарушение инкапсуляции во всей красе.
Использование геттера подразумевает, что вам нужно это значение, а на самом деле это не так.
Возможно, вам действительно нужно это улучшение производительности
В крайних случаях легких объектов, которые должны иметь максимально возможную производительность, возможно (хотя и крайне маловероятно), что вы не сможете заплатить очень маленькое снижение производительности, налагаемое геттером. Это не произойдет в 99,9% случаев.
источник
Generator
объект, который проходит через все моиItems
объекты, а затем вызываетgetName
каждый из них,Item
чтобы сделать что-то еще. В чем проблема с этим? Затем, в ответ,Generator
выплевывает отформатированные строки. Это в моей структуре, для которой у меня тогда есть API, который люди могут использовать для запуска всего, что предоставляют пользователи, но не касаясь структуры.What is the issue with this?
Ничего такого, что я вижу. По сути, это то, чтоmap
делает функция. Но это не тот вопрос, который вы задали. Вы по существу спросили: «Существуют ли условия, при которых добытчик может быть нежелательным». Я ответил двумя, но это не значит, что вы вообще отказываетесь от сеттеров.Детали реализации утечки геттеров и абстракция разрыва
Рассмотреть возможность
public int millisecondsSince1970()
Ваши клиенты будут думать: «О, это int», и будет раздача кол-ва вызовов, предполагающих, что это int , выполнение целочисленных математических сравнений дат и т. Д. Когда вы поймете, что вам нужно long , будет много устаревшего кода с проблемами. В мире Java вы добавляете
@deprecated
API, все его игнорируют, и вы застряли в поддержке устаревшего, глючного кода. :-( (я предполагаю, что другие языки похожи!)В этом конкретном случае я не уверен, что может быть лучшим вариантом, и ответ @candiedorange полностью уместен, во многих случаях вам нужны геттеры, но этот пример иллюстрирует недостатки. Каждый публичный получатель стремится «запереть» вас в определенной реализации. Используйте их, если вам действительно нужно «пересечь границы», но используйте как можно меньше, с осторожностью и предусмотрительностью.
источник
timepoint
иtimespan
соответственно. (epoch
является конкретным значениемtimepoint
.) В этих классах они предоставляют методы получения, такие какgetInt
илиgetLong
илиgetDouble
. Если классы, которые манипулируют временем, написаны так, что они (1) делегируют арифметику, связанную со временем, этим объектам и методам и (2) предоставляют доступ к этим объектам времени через геттеры, тогда проблема решается без необходимости избавления от геттеров. ,Я думаю, что первый ключ - помнить, что абсолюты всегда неправы.
Рассмотрим программу адресной книги, которая просто хранит контактную информацию людей. Но давайте сделаем это правильно и разделим его на уровни контроллера / службы / хранилища.
Будет
Person
класс, который содержит фактические данные: имя, адрес, номер телефона и т. Д.,Address
ИPhone
тоже классы, поэтому мы можем использовать полиморфизм позже для поддержки неамериканских схем. Все три из этих классов являются объектами передачи данных , поэтому они будут не более чем геттерами и сеттерами. И это имеет смысл: в них не будет никакой логики, кроме переопределенияequals
иgetHashcode
; просить их представить вам полную информацию о человеке не имеет смысла: для HTML-презентации, пользовательского приложения с графическим интерфейсом, консольного приложения, конечной точки HTTP и т. д.?Вот тут-то и вступают контроллер, сервис и репозиторий. Все эти классы будут тяжелыми для логики и получающими свет благодаря внедрению зависимостей.
Контроллер собирается внедрить сервис, который будет предоставлять такие методы, как
get
иsave
, оба из которых будут принимать некоторые разумные аргументы (например,get
могут иметь переопределения для поиска по имени, поиска по почтовому индексу или просто получить все;save
вероятно, потребуется одинPerson
и сохранить его). Какие геттеры будут иметь контроллер? Возможность получения имени конкретного класса может быть полезна для ведения журнала, но какое состояние он будет содержать, кроме состояния, которое было введено в него?Точно так же сервисный уровень получит встроенный репозиторий, поэтому он может получать и сохранять данные
Person
, и у него может быть некоторая «бизнес-логика» (например, возможно, мы собираемся требовать, чтобы у всехPerson
объектов был хотя бы один адрес или телефон число). Но опять же: в каком состоянии находится этот объект, кроме того, что вводится? Опять нет.На уровне хранилища все становится немного интереснее: он собирается установить соединение с хранилищем, например. файл, база данных SQL или хранилище в памяти. Здесь соблазнительно добавить геттер для получения имени файла или строки соединения SQL, но это состояние, которое было введено в класс. Должен ли репозиторий иметь геттер, чтобы определить, успешно ли он подключен к своему хранилищу данных? Ну, может быть, но какая польза от этой информации вне самого класса репозитория? Сам класс репозитория должен быть в состоянии попытаться переподключиться к своему хранилищу данных, если это необходимо, что говорит о том, что
isConnected
свойство уже имеет сомнительную ценность. Кроме того,isConnected
свойство, вероятно, будет неправильным именно тогда, когда оно наиболее необходимо: проверкаisConnected
прежде чем пытаться получить / сохранить данные, не гарантирует, что хранилище все еще будет подключено, когда будет выполнен «настоящий» вызов, поэтому это не устраняет необходимость обработки исключений где-то (есть аргументы для того, куда это должно идти, помимо объем этого вопроса).Рассмотрим также модульные тесты (вы пишете модульные тесты, верно?): Сервис не будет ожидать внедрения определенного класса, скорее он будет ожидать внедрения конкретной реализации интерфейса. Это позволяет приложению в целом изменить место хранения данных, не делая ничего, кроме замены хранилища, внедряемого в службу. Это также позволяет сервису тестироваться модулем путем внедрения фиктивного хранилища. Это означает думать с точки зрения интерфейсов, а не классов. Будет ли интерфейс репозитория выставлять какие-либо геттеры? То есть существует ли какое-либо внутреннее состояние, которое будет: общим для всех хранилищ, полезным вне хранилища и не внедренным в хранилище? Я был бы в затруднении, чтобы думать о любом сам.
TL; DR
В заключение: кроме классов, единственной целью которых является перенос данных, ничто другое в стеке не может поместить геттер: состояние либо внедрено, либо не имеет значения вне рабочего класса.
источник
Изменчивые участники.
Если у вас есть набор вещей в объекте, который вы выставляете через геттер, вы можете подвергнуть себя ошибкам, связанным с неправильными вещами, добавляемыми в коллекцию.
Если у вас есть изменяемый объект, который вы выставляете через геттер, вы потенциально открываете себя для изменения внутреннего состояния вашего объекта так, как вы этого не ожидаете.
Действительно плохим примером будет объект, который считает от 1 до 100, выставляя свое Текущее значение в качестве изменяемой ссылки. Это позволяет стороннему объекту изменять значение Current таким образом, чтобы значение могло лежать за пределами ожидаемых границ.
Это в основном проблема с отображением изменчивых объектов. Разоблачение структур или неизменяемых объектов не является проблемой вообще, поскольку любые изменения в них изменят вместо этого копию (обычно либо неявной копией в случае структуры, либо явной копией в случае неизменяемого объекта).
Использовать метафору, которая может помочь увидеть разницу.
У цветка есть несколько уникальных вещей. Он имеет
Color
и имеет несколько лепестков (NumPetals). Если я наблюдаю за этим цветком в реальном мире, я ясно вижу его цвет. Затем я могу принимать решения на основе этого цвета. Например, если цвет черный, не дайте подруге, но если цвет красный, дайте подруге. В нашей объектной модели цвет цветка будет выставлен как получатель на объекте цветка. Мои наблюдения за этим цветом важны для действий, которые я буду выполнять, но это никак не влияет на объект цветка. Я не должен быть в состоянии изменить цвет этого цветка. Точно так же не имеет смысла скрывать свойство color. Цветок, как правило, не может помешать людям наблюдать его цвет.Если я окрашиваю цветок, я должен вызвать метод ReactToDye (Color dyeColor) для цветка, который изменит цветок в соответствии с его внутренними правилами. Затем я могу снова запросить
Color
свойство и отреагировать на любое изменение в нем после вызова метода ReactToDye. Было бы неправильно для меня напрямую модифицироватьColor
цветок, и если бы я мог, тогда абстракция сломалась.Иногда (реже, но все же достаточно часто, что стоит упомянуть) сеттеры являются вполне допустимым ОО-дизайном. Если у меня есть объект Customer, вполне допустимо выставить сеттер для их адреса. Называете ли вы это
setAddress(string address)
илиChangeMyAddressBecauseIMoved(string newAddress)
простоstring Address { get; set; }
вопрос семантики. Внутреннее состояние этого объекта должно измениться, и соответствующий способ сделать это - установить внутреннее состояние этого объекта. Даже если мне потребуется историческая запись адресов, в которыхCustomer
он жил, я могу использовать установщик, чтобы соответствующим образом изменить свое внутреннее состояние. В этом случае не имеет смыслаCustomer
быть неизменным, и нет лучшего способа изменить адрес, чем предоставить установщик для этого.Я не уверен, кто предполагает, что геттеры и сеттеры являются плохими или нарушают объектно-ориентированные парадигмы, но если они это делают, они, вероятно, делают это в ответ на специфическую языковую функцию языка, который они используют. У меня возникает ощущение, что это выросло из Java-культуры "все является объектом" (и, возможно, распространялось на некоторые другие языки). Мир .NET вообще не обсуждает это. Методы получения и установки - это первоклассная языковая функция, которая используется не только людьми, пишущими приложения на этом языке, но и самим языковым API.
Вы должны быть осторожны при использовании добытчиков. Вы не хотите предоставлять неправильные фрагменты данных и не хотите предоставлять данные неверным образом (то есть изменяемые объекты, ссылки на структуры, которые могут позволить потребителю изменять внутреннее состояние). Но вы действительно хотите смоделировать свои объекты так, чтобы данные инкапсулировались таким образом, чтобы ваши объекты могли использоваться внешними потребителями и защищены от неправильного использования этими же потребителями. Часто это потребует добытчиков.
Подводя итог: геттеры и сеттеры являются действительными объектно-ориентированными инструментами проектирования. Они не должны использоваться настолько широко, что раскрываются все детали реализации, но при этом вы не хотите использовать их настолько экономно, чтобы потребители объекта не могли использовать объект эффективно.
источник
Ремонтопригодность сломается.
В любое время (я бы сказал почти всегда) вы предлагаете добытчика, который вы в основном отдаете больше, чем вас просили. Это также означает, что вы должны поддерживать больше, чем было запрошено, поэтому вы снижаете удобство обслуживания без какой-либо реальной причины.
Давайте рассмотрим
Collection
пример @ candied_orange . Вопреки тому, что он пишет,Collection
не должно быть добытчика. Коллекции существуют по очень конкретным причинам, прежде всего для перебора всех элементов. Эта функциональность ни для кого не является неожиданностью, поэтому она должна быть реализованаCollection
вместо того, чтобы навязывать пользователю этот очевидный вариант использования, используя циклы for и методы получения или чего-либо еще.Чтобы быть справедливыми, некоторые границы делают необходимость добытчиков. Это происходит, если вы действительно не знаете, для чего они будут использоваться. Например, в настоящее время вы можете программно получить трассировку стека из
Exception
класса Java , потому что люди хотели использовать его по разным любопытным причинам, которые авторы не могли или не могли представить.источник
Collection
s одновременно, как в(A1, B1), (A2, B2), ...
. Это требует реализации "почтового индекса". Как вы реализуете «zip», если на одном из двух реализована функция повторного наложенияCollection
- как получить доступ к соответствующему элементу в другом?Collection
Реализуетzip
сам с собой, в зависимости от того, как написана библиотека. Если этого не произойдет, вы реализуете его и отправляете запрос на извлечение создателю библиотеки. Обратите внимание, что я согласился, что некоторым границам иногда нужны геттеры, моя настоящая проблема в том, что геттеры сейчас везде.Collection
объекте, по крайней мере, для собственной реализацииzip
. (То есть получатель должен как минимум иметь видимость пакета.) И теперь вы зависите от того, будет ли создатель библиотеки принять ваш запрос на извлечение ...Collection
, Очевидно , имеет доступ к своему внутреннему состоянию, или быть более точными любойCollection
экземпляр может получить доступ к внутреннему состоянию любому другому. Это тот же тип, не нужны добытчики.