Инкапсуляция имеет цель, но она также может быть использована неправильно или злоупотреблять.
Рассмотрим что-то вроде Android API, в котором есть классы с десятками (если не сотнями) полей. Открывая эти поля, пользователь API усложняет навигацию и использование, а также дает пользователю ложное представление о том, что он может делать с этими полями все, что захочет, что может конфликтовать с тем, как они должны использоваться. Таким образом, инкапсуляция хороша в этом смысле для удобства обслуживания, удобства использования, читабельности и предотвращения сумасшедших ошибок.
С другой стороны, POD или простые старые типы данных, такие как структура из C / C ++, в которой все поля являются открытыми, также могут быть полезны. Наличие бесполезных методов получения / установки, подобных тем, которые генерируются аннотацией @data в Lombok, - это просто способ сохранить «шаблон инкапсуляции». Одна из немногих причин, по которой мы делаем «бесполезные» методы получения / установки в Java, заключается в том, что методы предоставляют контракт .
В Java вы не можете иметь поля в интерфейсе, поэтому вы используете геттеры и сеттеры для указания общего свойства, которое есть у всех разработчиков этого интерфейса. В более поздних языках, таких как Kotlin или C #, мы видим концепцию свойств как полей, для которых вы можете объявить установщик и получатель. В конце концов, бесполезные методы получения / установки являются более наследием, с которым приходится жить Java, если только Oracle не добавит в него свойства. Например, Kotlin, который является еще одним языком JVM, разработанным JetBrains, имеет классы данных, которые в основном выполняют то же, что и аннотация @data в Lombok.
Также вот несколько примеров:
class DataClass
{
private int data;
public int getData() { return data; }
public void setData(int data) { this.data = data; }
}
Это плохой случай инкапсуляции. Геттер и сеттер практически бесполезны. Инкапсуляция в основном используется, потому что это стандарт в таких языках, как Java. На самом деле не помогает, кроме поддержания согласованности всей базы кода.
class DataClass implements IDataInterface
{
private int data;
@Override public int getData() { return data; }
@Override public void setData(int data) { this.data = data; }
}
Это хороший пример инкапсуляции. Инкапсуляция используется для обеспечения исполнения контракта, в данном случае IDataInterface. Цель инкапсуляции в этом примере - заставить потребителя этого класса использовать методы, предоставляемые интерфейсом. Несмотря на то, что метод получения и установки не делает ничего особенного, мы теперь определили общую черту между DataClass и другими реализациями IDataInterface. Таким образом, у меня может быть такой метод:
void doSomethingWithData(IDataInterface data) { data.setData(...); }
Теперь, когда речь идет об инкапсуляции, я считаю важным также рассмотреть проблему синтаксиса. Я часто вижу, что люди жалуются на синтаксис, необходимый для обеспечения инкапсуляции, а не самой инкапсуляции. Один пример, который приходит на ум, - от Кейси Муратори (вы можете увидеть его напыщенную речь здесь ).
Предположим, у вас есть класс игрока, который использует инкапсуляцию и хотите переместить свою позицию на 1 единицу. Код будет выглядеть так:
player.setPosX(player.getPosX() + 1);
Без инкапсуляции это выглядело бы так:
player.posX++;
Здесь он утверждает, что инкапсуляция приводит к гораздо большему количеству печатания без дополнительных преимуществ, и во многих случаях это может быть правдой, но обратите внимание. Аргумент против синтаксиса, а не самой инкапсуляции. Даже в таких языках, как C, в которых отсутствует концепция инкапсуляции, вы часто будете видеть переменные в структурах с префиксом или с суффиксом '_' или 'my' или любым другим, чтобы показать, что они не должны использоваться потребителем API, как если бы они были частный.
В том-то и дело, что инкапсуляция может помочь сделать код более понятным и простым в использовании. Рассмотрим этот класс:
class VerticalList implements ...
{
private int posX;
private int posY;
... //other members
public void setPosition(int posX, int posY)
{
//change position and move all the objects in the list as well
}
}
Если бы переменные были общедоступны в этом примере, тогда потребитель этого 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:
data class DataClass(var data: Int)
Обратите внимание, что этот синтаксис позволяет нам делать Java Bean в одну строку. Вы правильно заметили проблему, которая существует в языке, подобном Java, при реализации инкапсуляции, но это вина Java не в самой инкапсуляции.
Вы сказали, что используете @Data Lombok для генерации геттеров и сеттеров. Обратите внимание на имя @Data. Он в основном предназначен для использования в классах данных, которые хранят только данные и предназначены для сериализации и десериализации. Придумайте что-то вроде файла сохранения из игры. Но в других сценариях, например, с элементом пользовательского интерфейса, вам определенно нужны сеттеры, потому что простого изменения значения переменной может быть недостаточно для получения ожидаемого поведения.
"It will create getters, setters and setting constructors for all private fields."
- То , как вы описываете этот инструмент, это звучит , как он будет поддерживать инкапсуляцию. (По крайней мере, в свободном, автоматизированном, несколько анемичном смысле.) Так в чем именно проблема?