Инкапсуляция говорит мне сделать все или почти все поля приватными и выставить их через getters / setters. Но теперь появляются такие библиотеки, как Lombok, которые позволяют нам раскрывать все приватные поля одной короткой аннотацией @Data
. Он создаст геттеры, сеттеры и конструкторы настроек для всех приватных полей.
Может ли кто-нибудь объяснить мне, в чем смысл скрывать все поля как частные и после этого раскрывать их все с помощью какой-то дополнительной технологии? Почему тогда мы просто не используем только открытые поля? Я чувствую, что мы прошли долгий и трудный путь, чтобы вернуться к исходной точке.
Да, есть другие технологии, которые работают через геттеры и сеттеры. И мы не можем использовать их через простые публичные поля. Но эти технологии появились только потому, что у нас были эти многочисленные свойства - частные поля за публичными получателями / установщиками. Если бы у нас не было свойств, эти технологии развивались бы по-другому и поддерживали бы общественные сферы. И все было бы просто, и теперь у нас не было бы необходимости в Ломбоке.
В чем смысл всего этого цикла? И имеет ли смысл инкапсуляция в программировании в реальной жизни?
источник
"It will create getters, setters and setting constructors for all private fields."
- То , как вы описываете этот инструмент, это звучит , как он будет поддерживать инкапсуляцию. (По крайней мере, в свободном, автоматизированном, несколько анемичном смысле.) Так в чем именно проблема?Ответы:
Если вы предоставляете все свои атрибуты с помощью методов получения / установки, вы получаете только структуру данных, которая все еще используется в Си или любом другом процедурном языке. Это не инкапсуляция, и Lombok просто делает работу с процедурным кодом менее болезненной. Получатели / сеттеры так же плохи, как и обычные открытые поля. На самом деле нет никакой разницы.
И структура данных не является объектом. Если вы начнете создавать объект с написания интерфейса, вы никогда не добавите получатели / установщики в интерфейс. Разоблачение ваших атрибутов приводит к процедурному коду спагетти, где манипулирование данными происходит вне объекта и распространяется по всей базе кода. Теперь вы имеете дело с данными и манипулируете ими, а не общаетесь с объектами. С геттерами / сеттерами у вас будет процедурное программирование, управляемое данными, где манипуляции с этим выполняются прямым императивным способом. Получить данные - сделать что-то - установить данные.
В ООП инкапсуляция - это слон, если все сделано правильно. Вы должны инкапсулировать состояние и детали реализации, чтобы объект имел полный контроль над этим. Логика будет сосредоточена внутри объекта и не будет распространяться по всей базе кода. И да - инкапсуляция все еще важна в программировании, так как код будет более понятным.
редактирует
Посмотрев на продолжающиеся дискуссии, я хочу добавить несколько вещей:
Поэтому, если мы вернемся к вопросу, почему мы должны это делать. Рассмотрим этот простой пример:
Здесь у нас есть Document, раскрывающий внутреннюю детали с помощью getter, и у нас есть внешний процедурный код в
printDocument
функции, который работает с этим вне объекта. Почему это плохо? Потому что теперь у вас есть только код в стиле C. Да, это структурировано, но в чем разница? Вы можете структурировать функции C в разных файлах и с именами. И эти так называемые слои делают именно это. Класс обслуживания - это просто набор процедур, которые работают с данными. Этот код менее понятен и имеет много недостатков.Сравните с этим. Теперь у нас есть контракт, и детали реализации этого контракта скрыты внутри объекта. Теперь вы можете действительно протестировать этот класс, и этот класс инкапсулирует некоторые данные. Как это работает с этими данными - вопрос объекта. Для того, чтобы поговорить с объектом сейчас вам нужно попросить его напечатать себя. Это инкапсуляция, и это объект. Вы получите всю мощь внедрения зависимостей, насмешек, тестирования, отдельных обязанностей и множества преимуществ с ООП.
источник
Смысл в том, что вы не должны этого делать .
Инкапсуляция означает, что вы открываете только те поля, к которым на самом деле нужны другие классы, и что вы очень избирательны и осторожны с этим.
Не надо просто давать все поля геттерам и сеттерам по умолчанию!
Это полностью противоречит духу спецификации JavaBeans, которая, по иронии судьбы, является источником концепции общедоступных методов получения и установки.
Но если вы посмотрите на спецификацию, то увидите, что создание геттеров и сеттеров было очень избирательным, и в нем говорится о свойствах «только для чтения» (без сеттера) и «только для записи» (нет геттер).
Другим фактором является то, что геттеры и сеттеры не обязательно имеют простой доступ к приватному полю . Получатель может вычислить значение, которое он возвращает, произвольно сложным способом, возможно, кэшировать его. Установщик может проверить значение или уведомить слушателей.
Итак, вы: инкапсуляция означает, что вы предоставляете только те функциональные возможности, которые вам действительно нужны. Но если вы не думаете о том, что вам нужно раскрыть, и просто волей-неволей разоблачаете все, следуя некоторой синтаксической трансформации, тогда, конечно, это не совсем инкапсуляция.
источник
getFieldName()
стали для нас автоматическим контрактом. Мы не будем ожидать какого-то сложного поведения за этим. В большинстве случаев это просто прямое нарушение инкапсуляции.Я думаю, что суть вопроса объясняется вашим комментарием:
Проблема в том, что вы смешиваете модель постоянных данных с активной моделью данных.
Приложение обычно имеет несколько моделей данных:
поверх модели данных, которую он фактически использует для выполнения своих вычислений.
В общем, модели данных, используемые для связи с внешней средой, должны быть изолированными и независимыми от внутренней модели данных (модели бизнес-объектов, спецификации), на которой выполняются вычисления:
В этом сценарии для объектов, используемых на коммуникационных уровнях, совершенно нормально, чтобы все элементы были общедоступными или открывались геттерами / сеттерами. Это простые объекты без какого-либо инварианта.
С другой стороны, ваша спецификация должна иметь инварианты, что, как правило, исключает наличие большого количества сеттеров (геттеры не влияют на инварианты, хотя они до некоторой степени уменьшают инкапсуляцию).
источник
Рассмотрим следующее ..
У вас есть
User
класс со свойствомint age
:Вы хотите увеличить это, чтобы
User
иметь дату рождения, а не только возраст. Используя геттер:Мы можем поменять
int age
поле на более сложноеLocalDate dateOfBirth
:Никаких нарушений контракта, никакого взлома кода. Не более, чем расширение внутреннего представительства при подготовке к более сложному поведению.
Само поле инкапсулировано.
Теперь, чтобы очистить проблемы ..
@Data
Аннотация Lombok похожа на классы данных Kotlin .Не все классы представляют поведенческие объекты. Что касается нарушения инкапсуляции, это зависит от вашего использования этой функции. Вы не должны выставлять все свои поля через геттеры.
В более общем смысле инкапсуляция - это акт сокрытия информации. Если вы злоупотребляете
@Data
, то легко предположить, что вы, вероятно, нарушаете инкапсуляцию. Но это не значит, что у него нет цели. JavaBeans, например, не одобряется некоторыми. Тем не менее, он широко используется в развитии предприятия.Сделаете ли вы вывод, что развитие предприятия плохо из-за использования бинов? Конечно, нет! Требования отличаются от требований стандартной разработки. Можно ли злоупотреблять бобами? Конечно! Они постоянно подвергаются насилию!
Lombok также поддерживает
@Getter
и@Setter
самостоятельно - использует то, что требуют ваши требования.источник
@Data
аннотации на тип, который по замыслу удаляет все поляsetAge(xyz)
Это не то, как инкапсуляция определяется в объектно-ориентированном программировании. Инкапсуляция означает, что каждый объект должен быть похож на капсулу, чья внешняя оболочка (общедоступные API) защищает и регулирует доступ к своей внутренней части (частные методы и поля) и скрывает ее от просмотра. Скрывая внутренние компоненты, вызывающие абоненты не зависят от внутренних, что позволяет изменять внутренние компоненты без изменения (или даже перекомпиляции) вызывающих абонентов. Кроме того, инкапсуляция позволяет каждому объекту применять свои собственные инварианты, делая только безопасные операции доступными для вызывающих.
Следовательно, инкапсуляция - это особый случай сокрытия информации, при котором каждый объект скрывает свои внутренние объекты и применяет свои инварианты.
Генерация геттеров и сеттеров для всех полей является довольно слабой формой инкапсуляции, поскольку структура внутренних данных не скрыта, и инварианты не могут быть применены. Он имеет то преимущество, что вы можете изменить способ внутреннего хранения данных (при условии, что вы можете преобразовывать данные в старую структуру и из нее), однако не нужно менять (или даже перекомпилировать) вызывающие программы.
Частично это связано с исторической аварией. Первое, что бросается в глаза, это то, что в Java выражения вызова метода и выражения доступа к полю синтаксически различаются на сайте вызова, то есть замена доступа к полю вызовом получателя или установщика нарушает API класса. Поэтому, если вам может понадобиться метод доступа, вы должны написать его сейчас или иметь возможность нарушить API. Это отсутствие поддержки свойств на уровне языка резко контрастирует с другими современными языками, особенно C # и EcmaScript .
Забастовка 2 состоит в том, что спецификация JavaBeans определяла свойства как получатели / установщики, поля не были свойствами. В результате большинство ранних корпоративных сред поддерживало методы получения / установки, но не поля. Это давно ушло в прошлое ( API персистентности Java (JPA) , проверка бинов , архитектура Java для привязки XML (JAXB) , Джексонвсе поля поддержки к настоящему времени просто прекрасны), но старые учебники и книги продолжают задерживаться, и не все знают, что все изменилось. Отсутствие поддержки свойств на уровне языка все еще может быть проблемой в некоторых случаях (например, потому что отложенная загрузка JPA отдельных сущностей не срабатывает при чтении открытых полей), но в основном открытые поля работают просто отлично. Иными словами, моя компания пишет все DTO для своих REST API с открытыми полями (в конце концов, она не становится общедоступной, передаваемой через Интернет :-).
Тем не менее, Lombok
@Data
делает больше, чем просто генерирует геттеры / сеттеры: он также генерируетtoString()
,hashCode()
иequals(Object)
, что может быть весьма полезным.Инкапсуляция может быть неоценимой или совершенно бесполезной, это зависит от инкапсулируемого объекта. Как правило, чем сложнее логика в классе, тем больше выгода от инкапсуляции.
Автоматически генерируемые геттеры и сеттеры для каждого поля, как правило, используются слишком часто, но могут быть полезны для работы с устаревшими платформами или для использования функции случайных структур, не поддерживаемой для полей.
Инкапсуляция может быть достигнута с помощью методов получения и команд. Установщики обычно не подходят, потому что ожидается, что они изменят только одно поле, в то время как поддержание инвариантов может потребовать изменения нескольких полей одновременно.
Резюме
геттеры / сеттеры предлагают довольно плохую инкапсуляцию.
Распространенность методов получения / установки в Java обусловлена отсутствием поддержки свойств на уровне языка и сомнительными вариантами проектирования в его исторической модели компонентов, которые закреплены во многих учебных материалах и программистах, которых они преподают.
Другие объектно-ориентированные языки, такие как EcmaScript, поддерживают свойства на уровне языка, поэтому геттеры могут быть введены без нарушения API. На таких языках геттеры могут быть введены тогда, когда они вам действительно нужны, а не заранее, просто на тот случай, когда вам может понадобиться один день, что делает процесс программирования гораздо более приятным.
источник
Я действительно задавал себе этот вопрос.
Хотя это не совсем так. Распространенность IMO геттеров / сеттеров обусловлена спецификацией Java Bean, которая требует этого; так что, если хотите, это в значительной степени особенность не объектно-ориентированного программирования, а бин-ориентированного программирования. Разница между ними заключается в уровне абстракции, в котором они существуют; Бины - это скорее системный интерфейс, т.е. на более высоком уровне. Они абстрагируются от основ ОО, или, по крайней мере, предназначены для того, чтобы, как всегда, все происходило слишком часто.
Я бы сказал, что несколько прискорбно, что эта вещь Bean, которая вездесуща в программировании на Java, не сопровождается добавлением соответствующей функции языка Java - я думаю о чем-то вроде концепции свойств в C #. Для тех, кто не знает об этом, это языковая конструкция, которая выглядит следующим образом:
В любом случае, практическая реализация все еще очень выигрывает от инкапсуляции.
источник
Простой ответ здесь: вы абсолютно правы. Получатели и установщики устраняют большую часть (но не все) значения инкапсуляции. Это не значит, что всякий раз, когда у вас есть метод get и / или set, который нарушает инкапсуляцию, но если вы слепо добавляете методы доступа ко всем закрытым членам вашего класса, вы делаете это неправильно.
Геттеры являются сеттерами повсеместно распространены в программировании на Java, потому что концепция JavaBean была предложена как способ динамического связывания функциональности с предварительно созданным кодом. Например, у вас может быть форма в апплете (кто-нибудь помнит те?), Которая будет проверять ваш объект, находить все свойства и отображать поля as. Затем пользовательский интерфейс может изменять эти свойства на основе пользовательского ввода. Вы, как разработчик, просто беспокоитесь о написании класса и размещаете там валидацию или бизнес-логику и т. Д.
Использование примеров Bean
Это не страшная идея как таковая, но я никогда не был большим поклонником подхода в Java. Это просто идет против зерна. Используйте Python, Groovy и т. Д. Что-то, что поддерживает такой подход более естественно.
JavaBean отчасти вышел из-под контроля, потому что он создал JOBOL, то есть Java-разработчиков, которые не понимают ОО. По сути, объекты стали не более чем пакетами данных, и вся логика была написана снаружи длинными методами. Поскольку это воспринималось как нормальное, такие люди, как вы и я, которые задают этот вопрос, считаются обманщиками. В последнее время я видел сдвиг, и это не такая уж посторонняя позиция
XML-привязка - крепкий орешек. Это, вероятно, не хорошее поле битвы для противостояния JavaBeans. Если вам нужно построить эти JavaBeans, постарайтесь не допустить их в настоящий код. Рассматривайте их как часть уровня сериализации.
источник
[Serializable]
атрибут и его родственников. Зачем писать 101 специфические методы / классы сериализации, когда вы можете просто написать один, используя рефлексию?Сколько мы можем сделать без добытчиков? Можно ли их полностью удалить? Какие проблемы это создает? Можем ли мы даже запретить
return
ключевое слово?Оказывается, вы можете многое сделать, если вы готовы сделать работу. Как же тогда информация выходит из этого полностью инкапсулированного объекта? Через сотрудника.
Вместо того, чтобы код задавал вам вопросы, вы говорите, что нужно делать. Если эти вещи также не возвращают вещи, вам не нужно ничего делать с тем, что они возвращают. Поэтому, когда вы думаете о поиске
return
вместо этого, попробуйте найти сотрудника выходного порта, который будет делать все, что осталось сделать.Такое поведение имеет свои преимущества и последствия. Вы должны думать не только о том, что бы вы вернули. Вы должны подумать о том, как вы собираетесь отправить это как сообщение объекту, который его не просил. Возможно, вы раздаете тот же объект, который вернули бы, или достаточно просто вызвать метод. Такое мышление обходится дорого.
Преимущество в том, что теперь вы разговариваете лицом к интерфейсу. Это означает, что вы получаете все преимущества абстракции.
Это также дает вам полиморфную рассылку, потому что, хотя вы знаете, что говорите, вам не нужно знать, что именно вы говорите.
Вы можете подумать, что это означает, что вам нужно пройти только один путь через стек слоев, но оказывается, что вы можете использовать полиморфизм, чтобы идти назад, не создавая сумасшедших циклических зависимостей.
Это может выглядеть так:
Если вы можете так кодировать, тогда использование геттеров - выбор. Не необходимое зло.
источник
Это спорный вопрос (как вы можете видеть), потому что куча догм и недоразумений смешана с разумной озабоченностью в отношении вопроса о получателях и установщиках. Короче говоря, в этом нет ничего плохого,
@Data
и это не нарушает инкапсуляцию.Зачем использовать геттеры и сеттеры, а не открытые поля?
Потому что геттеры / сеттеры обеспечивают инкапсуляцию. Если вы представляете значение как открытое поле, а затем переходите к вычислению значения на лету, вам необходимо изменить все клиенты, обращающиеся к полю. Очевидно, это плохо. Это деталь реализации, хранится ли какое-либо свойство объекта в поле, генерируется на лету или извлекается откуда-то еще, поэтому различие не должно быть открыто для клиентов. Геттер / сеттер-сеттер решают эту проблему, поскольку они скрывают реализацию.
Но если метод получения / установки просто отражает основное частное поле, не так ли плохо?
Нет! Дело в том, что инкапсуляция позволяет изменить реализацию, не влияя на клиентов. Поле могло бы все еще быть прекрасным способом сохранить ценность, пока клиенты не должны знать или заботиться.
Но не генерирует ли сам получатель / установщик из полей, нарушающих инкапсуляцию?
Нет инкапсуляции все еще там!
@Data
аннотации - это просто удобный способ написать пары getter / setter, которые используют базовое поле. Для точки зрения клиента это похоже на обычную пару геттер / сеттер. Если вы решите переписать реализацию, вы все равно можете сделать это, не затрагивая клиента. Таким образом, вы получаете лучшее из обоих миров: инкапсуляция и краткий синтаксис.Но некоторые говорят, что геттеры / сеттеры всегда плохи!
Существует отдельная дискуссия, в которой некоторые считают, что шаблон getter / setter всегда плох, независимо от базовой реализации. Идея состоит в том, что вы не должны устанавливать или получать значения от объектов, а любое взаимодействие между объектами должно моделироваться как сообщения, в которых один объект просит другой объект сделать что-то. Это в основном кусочек догмы с первых дней объектно-ориентированного мышления. Теперь мы думаем, что для определенных шаблонов (например, объектов значений, объектов передачи данных) геттеры / сеттеры могут быть вполне подходящими.
источник
Инкапсуляция имеет цель, но она также может быть использована неправильно или злоупотреблять.
Рассмотрим что-то вроде Android API, в котором есть классы с десятками (если не сотнями) полей. Открывая эти поля, пользователь API усложняет навигацию и использование, а также дает пользователю ложное представление о том, что он может делать с этими полями все, что захочет, что может конфликтовать с тем, как они должны использоваться. Таким образом, инкапсуляция хороша в этом смысле для удобства обслуживания, удобства использования, читабельности и предотвращения сумасшедших ошибок.
С другой стороны, POD или простые старые типы данных, такие как структура из C / C ++, в которой все поля являются открытыми, также могут быть полезны. Наличие бесполезных методов получения / установки, подобных тем, которые генерируются аннотацией @data в Lombok, - это просто способ сохранить «шаблон инкапсуляции». Одна из немногих причин, по которой мы делаем «бесполезные» методы получения / установки в Java, заключается в том, что методы предоставляют контракт .
В Java вы не можете иметь поля в интерфейсе, поэтому вы используете геттеры и сеттеры для указания общего свойства, которое есть у всех разработчиков этого интерфейса. В более поздних языках, таких как Kotlin или C #, мы видим концепцию свойств как полей, для которых вы можете объявить установщик и получатель. В конце концов, бесполезные методы получения / установки являются более наследием, с которым приходится жить Java, если только Oracle не добавит в него свойства. Например, Kotlin, который является еще одним языком JVM, разработанным JetBrains, имеет классы данных, которые в основном выполняют то же, что и аннотация @data в Lombok.
Также вот несколько примеров:
Это плохой случай инкапсуляции. Геттер и сеттер практически бесполезны. Инкапсуляция в основном используется, потому что это стандарт в таких языках, как Java. На самом деле не помогает, кроме поддержания согласованности всей базы кода.
Это хороший пример инкапсуляции. Инкапсуляция используется для обеспечения исполнения контракта, в данном случае IDataInterface. Цель инкапсуляции в этом примере - заставить потребителя этого класса использовать методы, предоставляемые интерфейсом. Несмотря на то, что метод получения и установки не делает ничего особенного, мы теперь определили общую черту между DataClass и другими реализациями IDataInterface. Таким образом, у меня может быть такой метод:
Теперь, когда речь идет об инкапсуляции, я считаю важным также рассмотреть проблему синтаксиса. Я часто вижу, что люди жалуются на синтаксис, необходимый для обеспечения инкапсуляции, а не самой инкапсуляции. Один пример, который приходит на ум, - от Кейси Муратори (вы можете увидеть его напыщенную речь здесь ).
Предположим, у вас есть класс игрока, который использует инкапсуляцию и хотите переместить свою позицию на 1 единицу. Код будет выглядеть так:
Без инкапсуляции это выглядело бы так:
Здесь он утверждает, что инкапсуляция приводит к гораздо большему количеству печатания без дополнительных преимуществ, и во многих случаях это может быть правдой, но обратите внимание. Аргумент против синтаксиса, а не самой инкапсуляции. Даже в таких языках, как C, в которых отсутствует концепция инкапсуляции, вы часто будете видеть переменные в структурах с префиксом или с суффиксом '_' или 'my' или любым другим, чтобы показать, что они не должны использоваться потребителем API, как если бы они были частный.
В том-то и дело, что инкапсуляция может помочь сделать код более понятным и простым в использовании. Рассмотрим этот класс:
Если бы переменные были общедоступны в этом примере, тогда потребитель этого API был бы озадачен тем, когда использовать posX и posY и когда использовать setPosition (). Скрывая эти детали, вы помогаете потребителю лучше использовать ваш API интуитивно понятным способом.
Хотя синтаксис является ограничением во многих языках. Однако более новые языки предлагают свойства, которые дают нам приятный синтаксис публикуемых членов и преимущества инкапсуляции. Вы найдете свойства в C #, Kotlin, даже в C ++, если вы используете MSVC. Вот пример в Котлине.
class VerticalList: ... {var posX: Int set (x) {field = x; ...} var posY: Int set (y) {field = y; ...}}
Здесь мы достигли того же, что и в примере с Java, но мы можем использовать posX и posY, как если бы они были открытыми переменными. Когда я пытаюсь изменить их значение, будет выполнено тело setter ().
Например, в Kotlin это будет эквивалент Java Bean с реализованными getter, setters, hashcode, equals и toString:
Обратите внимание, что этот синтаксис позволяет нам делать Java Bean в одну строку. Вы правильно заметили проблему, которая существует в языке, подобном Java, при реализации инкапсуляции, но это вина Java не в самой инкапсуляции.
Вы сказали, что используете @Data Lombok для генерации геттеров и сеттеров. Обратите внимание на имя @Data. Он в основном предназначен для использования в классах данных, которые хранят только данные и предназначены для сериализации и десериализации. Придумайте что-то вроде файла сохранения из игры. Но в других сценариях, например, с элементом пользовательского интерфейса, вам определенно нужны сеттеры, потому что простого изменения значения переменной может быть недостаточно для получения ожидаемого поведения.
источник
Инкапсуляция дает вам гибкость . Разделяя структуру и интерфейс, она позволяет изменять структуру без изменения интерфейса.
Например, если вы обнаружите, что вам нужно вычислить свойство на основе других полей, а не инициализировать базовое поле при построении, вы можете просто изменить геттер. Если бы вы открыли поле напрямую, вам нужно было бы изменить интерфейс и вносить изменения на каждом сайте использования.
источник
Я попытаюсь проиллюстрировать проблемное пространство инкапсуляции и дизайна классов, и в конце отвечу на ваш вопрос.
Как упоминалось в других ответах, цель инкапсуляции состоит в том, чтобы скрыть внутренние детали объекта за открытым API, который служит контрактом. Объект безопасен для изменения своих внутренних объектов, потому что он знает, что он вызывается только через публичный API.
Имеет ли смысл иметь открытые поля, или методы получения / установки, или методы транзакций более высокого уровня, или передачу сообщений, зависит от природы моделируемого домена. В книге Akka Concurrency (которую я могу порекомендовать, даже если она несколько устарела) вы найдете пример, который иллюстрирует это, и я сокращу его здесь.
Рассмотрим класс User:
Это прекрасно работает в однопоточном контексте. Моделируемый домен - это имя человека, и механизм установки этого имени может быть идеально инкапсулирован сеттерами.
Однако представьте, что это должно быть обеспечено в многопоточном контексте. Предположим, один поток периодически читает имя:
И два других потока борются за перетягивание каната, устанавливая это по очереди Хиллари Клинтон и Дональду Трампу . Каждый из них должен вызвать два метода. В основном это работает хорошо, но время от времени вы будете видеть, как мимо проходит Хиллари Трамп или Дональд Клинтон .
Вы не можете решить эту проблему, добавив блокировку внутри сеттеров, потому что блокировка удерживается только в течение времени установки имени или фамилии. Единственное решение с помощью блокировки - это добавить блокировку вокруг всего объекта, но это нарушит инкапсуляцию, поскольку вызывающий код должен управлять блокировкой (и может вызвать взаимные блокировки).
Как оказалось, нет чистого решения через блокировку. Чистое решение состоит в том, чтобы снова инкапсулировать внутренние органы, делая их более грубыми:
Само имя стало неизменным, и вы видите, что его члены могут быть общедоступными, потому что теперь это чистый объект данных без возможности его изменения после создания. В свою очередь, открытый API класса User стал более грубым, оставив только один установщик, так что имя можно изменить только целиком. Он инкапсулирует больше своего внутреннего состояния за API.
В этом цикле вы видите попытки применить решения, подходящие для определенного набора обстоятельств, слишком широко. Подходящий уровень инкапсуляции требует понимания моделируемой области и применения правильного уровня инкапсуляции. Иногда это означает, что все поля являются открытыми, иногда (как в приложениях Akka) это означает, что у вас вообще нет общедоступного API, за исключением одного метода получения сообщений. Однако сама концепция инкапсуляции, означающая скрытие внутренних компонентов за стабильным API, является ключом к программному обеспечению в масштабе, особенно в многопоточных системах.
источник
Я могу вспомнить один случай использования, где это имеет смысл. У вас может быть класс, к которому вы изначально обращаетесь через простой API-интерфейс getter / setter. Позже вы расширяете или модифицируете так, чтобы он больше не использовал те же поля, но все еще поддерживал тот же API .
Несколько надуманный пример: точка, которая начинается как декартова пара с
p.x()
иp.y()
. Позже вы создаете новую реализацию или подкласс, который использует полярные координаты, поэтому вы также можете вызыватьp.r()
иp.theta()
, но ваш клиентский код, который вызываетp.x()
иp.y()
остается действительным. Сам класс прозрачно преобразуется из внутренней полярной формы, то есть,y()
теперьreturn r * sin(theta);
. (В этом примере установка толькоx()
илиy()
не имеет большого смысла, но все же возможна.)В этом случае вы можете сказать: «Рад, что я потрудился автоматически объявить методы получения и установки вместо того, чтобы обнародовать поля, иначе мне пришлось бы взломать мой API».
источник
Нет абсолютно никакого смысла. Однако тот факт, что вы задаете этот вопрос, демонстрирует, что вы не поняли, что делает Lombok, и что вы не понимаете, как писать ОО-код с инкапсуляцией. Давайте немного перемотать ...
Некоторые данные для экземпляра класса всегда будут внутренними и никогда не должны быть представлены. Некоторые данные для экземпляра класса необходимо будет установить извне, а некоторые данные, возможно, придется передать обратно из экземпляра класса. Мы можем захотеть изменить то, как класс работает под поверхностью, поэтому мы используем функции, которые позволяют нам получать и устанавливать данные.
Некоторые программы хотят сохранить состояние для экземпляров классов, поэтому они могут иметь некоторый интерфейс сериализации. Мы добавляем больше функций, которые позволяют экземпляру класса сохранять свое состояние в хранилище и извлекать его состояние из хранилища. Это поддерживает инкапсуляцию, потому что экземпляр класса все еще контролирует свои собственные данные. Мы можем сериализовать частные данные, но остальная часть программы не имеет доступа к ним (или, точнее, мы поддерживаем Китайскую стену , решив не намеренно повреждать эти личные данные), а экземпляр класса может (и должен) проводить проверки целостности при десериализации, чтобы убедиться, что его данные возвращены в порядке.
Иногда данные требуют проверки диапазона, проверки целостности или тому подобного. Написание этих функций позволяет нам делать все это. В этом случае мы не хотим или не нуждаемся в Ломбоке, потому что мы делаем все это сами.
Тем не менее, часто вы обнаруживаете, что внешний параметр хранится в одной переменной. В этом случае вам понадобятся четыре функции для получения / установки / сериализации / десериализации содержимого этой переменной. Написание этих четырех функций самостоятельно каждый раз замедляет работу и подвержено ошибкам. Автоматизация процесса с Lombok ускоряет вашу разработку и устраняет возможность ошибок.
Да, было бы возможно сделать эту переменную общедоступной. В этой конкретной версии кода он будет функционально идентичен. Но вернемся к тому, почему мы используем функции: «Мы можем захотеть изменить то, как класс работает под поверхностью». Если вы сделаете вашу переменную общедоступной, вы ограничите свой код сейчас и навсегда, чтобы эта открытая переменная была интерфейс. Если вы используете функции или если вы используете Lombok для автоматического создания этих функций для вас, вы можете в любое время изменить базовые данные и базовую реализацию.
Это делает это более ясным?
источник
Я на самом деле не Java-разработчик; но следующее в значительной степени не зависит от платформы.
Почти все, что мы пишем, использует публичные методы получения и установки, которые обращаются к частным переменным. Большинство геттеров и сеттеров тривиальны. Но когда мы решаем, что установщик должен что-то пересчитать, или установщик выполняет некоторую проверку, или нам нужно перенаправить свойство в свойство переменной-члена этого класса, это полностью не нарушает весь код и является двоично-совместимым, поэтому мы может поменять этот один модуль.
Когда мы решаем, что это свойство действительно должно быть вычислено на лету, весь код, который на него смотрит, не должен изменяться, и только код, который пишет в него, должен быть изменен, и IDE может найти его для нас. Когда мы решаем, что это доступное для записи вычисляемое поле (это когда-либо приходилось делать несколько раз), мы тоже можем это сделать. Приятно, что некоторые из этих изменений являются двоично-совместимыми (переход на вычисляемое поле только для чтения не является теоретическим, но в любом случае может быть на практике).
В итоге мы получили множество тривиальных геттеров со сложными сеттерами. Мы также закончили с довольно многими получателями кэширования. В результате вы можете предположить, что геттеры достаточно дешевы, а сеттеры - нет. С другой стороны, мы достаточно мудры, чтобы решить, что сеттеры не сохраняются на диске.
Но мне пришлось отследить парня, который вслепую менял все переменные-члены на свойства. Он не знал, что такое атомарное добавление, поэтому он изменил то, что действительно должно было быть публичной переменной, на свойство и аккуратно нарушил код.
источник
Методы получения и установки - это мера «на всякий случай», добавленная во избежание рефакторинга в будущем, если внутренняя структура или требования доступа изменятся в процессе разработки.
Скажем, через несколько месяцев после выпуска ваш клиент сообщает вам, что для одного из полей класса иногда задаются отрицательные значения, хотя в этом случае оно должно быть не более 0. Используя открытые поля, вам придется искать каждое назначение этого поля во всей вашей кодовой базе, чтобы применить функцию ограничения к значению, которое вы собираетесь установить, и помните, что вам всегда придется делать это при изменении этого поля, но это отстой. Вместо этого, если бы вы уже использовали методы получения и установки, вы могли бы просто изменить свой метод setField (), чтобы гарантировать, что это ограничение всегда применяется.
Теперь проблема с «устаревшими» языками, такими как Java, состоит в том, что они поощряют использование методов для этой цели, что просто делает ваш код бесконечно более многословным. Писать больно и трудно, поэтому мы использовали IDE, так или иначе смягчающие эту проблему. Большинство IDE автоматически генерирует для вас методы получения и установки, а также скрывает их, если не указано иное. Lombok делает еще один шаг и просто генерирует их процедурно во время компиляции, чтобы сделать ваш код более гладким. Однако другие, более современные языки просто так или иначе решили эту проблему. Языки Scala или .NET, например,
Например, в VB .NET или C # вы могли бы просто сделать все поля, предназначенные для простого -не побочного эффекта, получателями и установщиками, просто открытыми полями, а затем сделать их закрытыми, изменить их имя и предоставить свойство с прежним именем поле, где вы можете точно настроить поведение доступа к полю, если вам это нужно. С Lombok, если вам нужно точно настроить поведение метода получения или установки, вы можете просто удалить эти теги, когда это необходимо, и написать свой собственный с новыми требованиями, зная, что вам не придется ничего реорганизовывать в других файлах.
По сути, способ, которым ваш метод обращается к полю, должен быть прозрачным и равномерным. Современные языки позволяют определять «методы» с одним и тем же синтаксисом доступа / вызова поля, поэтому эти изменения могут быть выполнены по требованию, не задумываясь об этом на раннем этапе разработки, но Java вынуждает вас выполнять эту работу заранее, потому что это делает не имеют этой функции. Все, что делает Lombok, - это экономит ваше время, потому что используемый вами язык не позволяет вам сэкономить время для метода «на всякий случай».
источник
foo.bar
но он может быть обработан вызовом метода. Вы утверждаете, что это превосходит «устаревший» способ отображения API как вызовы методовfoo.getBar()
. Кажется, мы согласны с тем, что открытые поля проблематичны, но я утверждаю, что «устаревшая» альтернатива превосходит «современную», поскольку весь наш API симметричен (это все вызовы методов). В «современном» подходе мы должны решить, какие вещи должны быть свойствами, а какие - методами, которые все усложняют (особенно, если мы используем отражение!).Да, это противоречиво. Я впервые столкнулся со свойствами в Visual Basic. До этого на других языках, по моему опыту, не было никаких оберток собственности вокруг полей. Просто общественные, частные и защищенные поля.
Свойства были своего рода инкапсуляцией. Я понимал, что свойства Visual Basic - это способ контролировать и манипулировать выводом одного или нескольких полей, скрывая явное поле и даже его тип данных, например, отправляя дату в виде строки в определенном формате. Но даже тогда нельзя «скрывать состояние и раскрывать функциональность» с точки зрения более крупного объекта.
Но свойства оправдывают себя из-за отдельных методов получения и установки свойств. Экспонировать поле без свойства - это все или ничего - если вы можете прочитать его, вы можете изменить его. Так что теперь можно оправдать слабый дизайн класса защищенными сеттерами.
Так почему же мы не использовали настоящие методы? Потому что это был Visual Basic (и VBScript ) (оооо! Аааа!), Кодирование для масс (!), И это было все в ярости. И, таким образом, в конечном итоге доминирует идиократия.
источник