Menu
Coddy logo textTech

Геттеры и сеттеры: подробный разбор

Часть раздела Объектно-ориентированное программирование путешествия по 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;  // Только для чтения
}

Этот паттерн предоставляет внешнему коду видимость данных, предотвращая при этом любые изменения — приватное поле в сочетании только с геттером создает настоящий доступ только для чтения.

challenge icon

Задание

Легко

Давайте создадим систему инвентаризации товаров, которая продемонстрирует возможности геттеров и сеттеров для управления доступом к данным. Вы создадите класс, который защищает свои внутренние данные, предоставляя при этом вычисляемые свойства и валидацию.

Вы организуете свой код в двух файлах:

  • 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]"
}
quiz iconПроверьте себя

В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.

Все уроки раздела Объектно-ориентированное программирование