Геттеры и сеттеры
Геттеры и сеттеры (от англ. getter — «получатель» и setter — «установщик») — в объектно-ориентированном программировании (ООП) методы класса, предназначенные для контролируемого доступа к закрытым (private) или защищённым (protected) полям объекта. Геттер возвращает значение поля (обычно имеет сигнатуру вида getFieldName()), сеттер присваивает полю новое значение (обычно setFieldName(значение)). Использование геттеров и сеттеров позволяет реализовать инкапсуляцию, скрывая внутреннее состояние объекта от внешнего кода и обеспечивая возможность проверки данных, вычислений и логирования при записи или чтении.
История возникновения
Концепция сокрытия внутреннего состояния объекта и предоставления доступа к нему через публичные методы восходит к идеям структурного программирования и ранним объектно-ориентированным языкам, таким как Simula (1960-е) и Smalltalk (1970-е). В этих языках поля объекта по умолчанию были защищены, а для чтения и записи использовались сообщения-запросы. Систематическое применение геттеров и сеттеров стало нормой с развитием языков C++ (1985) и Java (1995), где модификаторы доступа (private, protected, public) формализовали разделение интерфейса и реализации. В 1990-е годы паттерн JavaBeans закрепил соглашение об именовании: getXxx() для геттеров и setXxx() для сеттеров.
Принципы инкапсуляции
Геттеры и сеттеры — основной инструмент инкапсуляции. Открывая прямое поле (например, obj.x = 5), программист теряет контроль над тем, какие значения может принять поле. Сеттер позволяет:
- Проверять входные данные (валидация) — например, отбрасывать отрицательный возраст.
- Генерировать события или вести логи.
- Преобразовывать внутреннее представление (например, хранить температуру в кельвинах, а сеттер принимать в градусах Цельсия).
Геттер, в свою очередь, может:
- Возвращать вычисляемое значение, а не хранимое поле.
- Возвращать копию внутренней структуры (например, массива), чтобы предотвратить её изменение извне.
- Осуществлять ленивую инициализацию.
Однако в некоторых случаях, особенно в простых структурах данных (например, DTO, POJO), использование геттеров и сеттеров без какой-либо логики может быть формальностью: некоторые языки (Kotlin, C#, Python) предоставляют синтаксис свойств, который позволяет объявить поле с неявным геттером и сеттером и при необходимости доопределить их поведение.
Реализация в различных языках программирования
Java
В Java геттеры и сеттеры — стандартная практика. Обычно они объявляются в составе POJO-классов (Plain Old Java Object). Пример: ```java public class Person { private String name; private int age;
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public int getAge() { return age; }
public void setAge(int age) { if (age >= 0) { this.age = age; } } } `` Для логических полей соглашение JavaBeans предписывает именовать геттер через is (например, isActive()`).
C#
В C# поддержка свойств (properties) делает геттеры и сеттеры частью синтаксиса языка: ``csharp public class Person { private string _name; public string Name { get { return _name; } set { _name = value; } } } ` В C# 3.0 появились автоматические свойства: public string Name { get; set; }`, которые не требуют явного поля.
Python
В Python прямое обращение к атрибутам — норма. Геттеры и сеттеры реализуются через декораторы @property, @<имя>.setter: ```python class Person: def __init__(self, name): self._name = name
@property def name(self): return self._name
@name.setter def name(self, value): if value is not None: self._name = value ``` Такой подход позволяет сначала использовать прямой атрибут, а затем безболезненно ввести валидацию.
C++
В C++ геттеры и сеттеры — обычные публичные методы, поскольку синтаксиса свойств нет: ``cpp class Person { private: std::string name; public: std::string getName() const { return name; } void setName(const std::string& newName) { name = newName; } }; ``
JavaScript (ES6)
В ES6 были введены акцессоры (геттеры и сеттеры) через ключевые слова get и set: ``javascript class Person { constructor(name) { this._name = name; } get name() { return this._name; } set name(value) { if (value) { this._name = value; } } } ``
Достоинства
- Контроль доступа: возможность запретить запись (только геттер) или чтение (только сеттер).
- Валидация: защита от некорректных данных.
- Абстрагирование внутренней реализации: изменение внутреннего представления поля (например, замена поля на вычисление) не требует изменения клиентского кода.
- Совместимость: многие библиотеки и фреймворки (сериализация, ORM, компонентные модели) ожидают наличие методов
get/setили свойств. - Логирование, отладка, события: удобно отслеживать изменения.
Критика и недостатки
- Избыточность: в простых моделях без логики геттеры и сеттеры увеличивают объём кода без пользы. Языки с автоматическими свойствами решили эту проблему частично.
- Нарушение принципа Law of Demeter: чрезмерное использование геттеров для развёртывания вложенных объектов может привести к «поездам» вызовов (
getA().getB().getC()). - Отсутствие гарантии инкапсуляции: сеттер, копирующий значение напрямую, не защищает от неявных изменений, если поле хранит ссылку на мутабельный объект.
- Иммутабельность: для неизменяемых классов сеттеры не нужны вовсе; вместо них используются фабрики или строители (builder pattern).
Альтернативные подходы
- Свойства (properties): синтаксические конструкции, объединяющие геттер и сеттер, часто с автоматической генерацией бэк-поля (C#, Python, Kotlin).
- Прямой открытый доступ: применяется в структурах данных и интерфейсах функциональных языков (например, Go, Rust), где инкапсуляция не обязательна.
- Builder-паттерн: используется для объектов с большим количеством полей, где вместо сеттеров вызываются цепочечные методы (например,
Person.builder().name("Иван").age(30).build()). - Замена геттера/сеттера вычисляемым свойством: в языках без свойств — метод с возвратом, который скрывает вычисления.
Применение в современных парадигмах
В реактивном программировании геттеры и сеттеры иногда заменяются наблюдаемыми полями (Observable), где запись в сеттер автоматически оповещает подписчиков. В функциональных языках с неизменяемыми данными (Haskell, F#, Clojure) геттеры и сеттеры заменяются линзами (lenses) — комбинаторами для чтения и создания новых версий структур.
Несмотря на критику, геттеры и сеттеры остаются фундаментальным приёмом в большинстве промышленных языков ООП, особенно в сочетании с tools-генерацией (Lombok для Java) и auto-properties в современных версиях C# и Kotlin.
Источники
- Эккель Б. «Философия Java». 4-е издание. Глава «Инкапсуляция».
- Мартин Р. «Чистый код. Создание, анализ и рефакторинг».
- Гамма Э., Хелм Р., Джонсон Р., Влиссидес Дж. «Приёмы объектно-ориентированного проектирования. Паттерны проектирования».
- Документация Oracle по JavaBeans и Java-свойствам.
- Спецификация языка C# (версия 12).
BFOmetr — база данных и аналитика по компаниям России.
На главную BFOmetr →