Геттеры и сеттеры: подробный разбор
Часть раздела Объектно-ориентированное программирование путешествия по Dart на Coddy — урок 33 из 110.
Геттеры и сеттеры становятся мощными инструментами инкапсуляции в сочетании с приватными полями. Они позволяют предоставить чистый публичный интерфейс, сохраняя при этом полный контроль над тем, как осуществляется доступ к данным и их изменение.
Распространенным паттерном является сочетание приватного поля с публичным геттером и сеттером, который включает валидацию:
class Temperature {
double _celsius = 0;
double get celsius => _celsius;
set celsius(double value) {
if (value < -273.15) {
_celsius = -273.15; // Минимум абсолютного нуля
} else {
_celsius = value;
}
}
}Сеттеры могут обеспечивать соблюдение бизнес-правил, гарантируя, что ваш объект никогда не перейдет в недопустимое состояние.
Вы также можете создавать вычисляемые геттеры, которые извлекают значения из приватных данных:
class Rectangle {
double _width;
double _height;
Rectangle(this._width, this._height);
double get area => _width * _height; // Вычисляемое значение
double get perimeter => 2 * (_width + _height);
double get width => _width;
set width(double value) {
if (value > 0) _width = value;
}
}Иногда вам нужен доступ только для чтения. Просто предоставьте геттер без сеттера:
class Order {
final DateTime _createdAt = DateTime.now();
DateTime get createdAt => _createdAt; // Только для чтения
}Этот паттерн предоставляет внешнему коду видимость данных, предотвращая при этом любые изменения — приватное поле в сочетании только с геттером создает настоящий доступ только для чтения.
Задание
ЛегкоДавайте создадим систему инвентаризации товаров, которая продемонстрирует возможности геттеров и сеттеров для управления доступом к данным. Вы создадите класс, который защищает свои внутренние данные, предоставляя при этом вычисляемые свойства и валидацию.
Вы организуете свой код в двух файлах:
product.dart: Определите классProduct, который управляет инвентарем с надлежащей инкапсуляцией. Ваш продукт должен иметь:- Публичное поле
String name - Приватное поле
double _price - Приватное поле
int _quantity - Конструктор, принимающий все три значения
- Геттер
price, который возвращает приватную цену - Сеттер
price, который принимает только значения больше0(игнорируйте недопустимые значения) - Геттер
quantity, который возвращает приватное количество - Сеттер
quantity, который принимает только значения0или больше (игнорируйте отрицательные значения) - Вычисляемый геттер
totalValue, который возвращает_price * _quantity - Вычисляемый геттер
isInStock, который возвращаетtrue, если количество больше0 - Геттер только для чтения
sku, который генерирует простой SKU, возвращая первые 3 символа имени в верхнем регистре, за которыми следует количество (например,LAP25для "Laptop" с количеством 25) - Метод
displayProduct(), который показывает детали продукта
- Публичное поле
main.dart: Импортируйте ваш класс продукта и продемонстрируйте, как геттеры и сеттеры управляют доступом:- Создайте продукт с именем
'Laptop', ценой999.99и количеством25 - Вызовите
displayProduct() - Выведите
In stock: [isInStock] - Выведите
SKU: [sku] - Попытайтесь установить цену
-50.0(должно быть проигнорировано) - Установите цену
899.99(допустимое изменение) - Попытайтесь установить количество
-10(должно быть проигнорировано) - Установите количество
0(допустимое изменение) - Вызовите
displayProduct() - Выведите
In stock: [isInStock] - Выведите
SKU: [sku]
- Создайте продукт с именем
Метод displayProduct() должен выводить данные в следующем формате:
--- Product Info ---
Name: [name]
Price: $[price]
Quantity: [quantity]
Total Value: $[totalValue]Ожидаемый вывод:
--- Product Info ---
Name: Laptop
Price: $999.99
Quantity: 25
Total Value: $24999.75
In stock: true
SKU: LAP25
--- Product Info ---
Name: Laptop
Price: $899.99
Quantity: 0
Total Value: $0.0
In stock: false
SKU: LAP0Шпаргалка
Геттеры и сеттеры в сочетании с приватными полями обеспечивают инкапсуляцию и контроль над доступом к данным.
Приватное поле с геттером и сеттером
class Temperature {
double _celsius = 0;
double get celsius => _celsius;
set celsius(double value) {
if (value < -273.15) {
_celsius = -273.15;
} else {
_celsius = value;
}
}
}Вычисляемые геттеры
Геттеры могут извлекать значения из приватных данных:
class Rectangle {
double _width;
double _height;
Rectangle(this._width, this._height);
double get area => _width * _height;
double get perimeter => 2 * (_width + _height);
double get width => _width;
set width(double value) {
if (value > 0) _width = value;
}
}Доступ только для чтения
Предоставьте геттер без сеттера для свойств, предназначенных только для чтения:
class Order {
final DateTime _createdAt = DateTime.now();
DateTime get createdAt => _createdAt;
}Попробуйте сами
import 'product.dart';
void main() {
// TODO: Создайте продукт с именем 'Laptop', ценой 999.99 и количеством 25
// TODO: Вызовите displayProduct()
// TODO: Выведите "In stock: [isInStock]"
// TODO: Выведите "SKU: [sku]"
// TODO: Попробуйте установить цену -50.0 (должно быть проигнорировано)
// TODO: Установите цену 899.99 (допустимое изменение)
// TODO: Попробуйте установить количество -10 (должно быть проигнорировано)
// TODO: Установите количество 0 (допустимое изменение)
// TODO: Вызовите displayProduct()
// TODO: Выведите "In stock: [isInStock]"
// TODO: Выведите "SKU: [sku]"
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
1Основы ООП
Внешние файлыБиблиотеки и импортВведение в ООПКлассы и объектыКлючевое слово thisМетодыПеременные экземпляраОсновы конструкторовИтоги — Простой калькулятор4Null-безопасность
Введение в Null-безопасностьNullable и Non-Nullable типыОператоры ? и !Ключевое слово Late и Null-безопасностьNull-aware операторыNull-безопасность в классахИтоги — Система профилей пользователей7Абстрактные классы и интерфейсы
Абстрактные классыАбстрактные методыИнтерфейсы в DartНеявные интерфейсыРеализация против наследованияМножественные интерфейсыИтоги — Калькулятор фигур10Коллекции и обобщения
Обзор List, Set, MapТипобезопасные коллекцииОбобщенные классыОбобщенные методыОграничения обобщенийIterable и IteratorИтоги: Обобщенное хранилище13Продвинутые концепции ООП
Композиция против наследованияМетоды расширенияВызываемые классыЗапечатанные классы (Dart 3)Записи (Dart 3)Паттерны и сопоставление (3.0)Перечисления с методами16Проект: Управление библиотекой
Обзор проектаКлассы Book и User2Конструкторы в Dart
Конструктор по умолчаниюИменованные конструкторыСписки инициализацииКонстантные конструкторыФабричные конструкторыПеренаправляющие конструкторыИтоги — Shape Builder5Инкапсуляция
Публичные и приватные членыСоглашение о префиксе _Приватность на уровне библиотекГеттеры и сеттеры: подробный разборСкрытие данныхИтоги — Записи о студентах8Mixins
Введение в MixinsСоздание MixinsИспользование нескольких MixinsКлючевое слово on в MixinsMixin и наследованиеMixin и интерфейсИтоги: Система животных11Специальные методы
Переопределение toString()Переопределение hashCode и ==Интерфейс ComparableМетод call()Переопределение noSuchMethodПовторение — Пользовательская коллекция14Паттерны проектирования. Часть 1
Введение в паттерны проектированияПаттерн SingletonПаттерн FactoryПаттерн ObserverПаттерн Strategy3Свойства классов
Члены экземпляра и статические членыПоля Final и ConstПеременные LateСтатические методы и поляГеттеры и сеттерыИтоги — Менеджер банковского счета6Наследование
Основы наследованияКлючевое слово superПереопределение методовАннотация @overrideКлючевое слово final для классовКонструкторы и наследованиеИтоги: Иерархия сотрудников9Полиморфизм
Основы полиморфизмаПолиморфизм через интерфейсыПроверка типов во время выполненияОператоры is и asКлючевое слово covariantИтоги — Обработчик платежей12Асинхронное ООП
Futures и async/awaitОсновы StreamsКонтроллеры StreamАсинхронные конструкторыАсинхронность в методах классовИтоги — Data Fetcher15Паттерны проектирования. Часть 2
Паттерн КомандаПаттерн АдаптерПаттерн ДекораторПаттерн Шаблонный методПаттерн СостояниеПаттерн КомпоновщикПаттерн Репозиторий