🧑🏫10. Инкапсуляция
Инкапсуляция — это принцип, который позволяет скрывать детали внутренней работы объектов и предоставлять только необходимый функционал другим частям программы. Это как коробка с кнопками на поверхности, где внешнему миру доступны лишь кнопки, но внутренние детали и механизмы коробки скрыты.
Зачем нужна инкапсуляция?
Защищает данные: предотвращает случайные или неправильные изменения данных извне.
Упрощает использование: делает объекты проще и понятнее для других частей программы.
Сохраняет стабильность кода: позволяет менять внутреннюю реализацию без изменения внешнего интерфейса, что уменьшает риск поломок.
Как создавать инкапсуляцию в Dart
Использование приватных переменных и методов — в Dart переменные и методы становятся приватными, если их имя начинается с
_
(например,_balance
).Методы для доступа к данным (геттеры и сеттеры): создаем публичные методы, чтобы безопасно читать и изменять приватные переменные.
Примеры с аналогиями
Банковский счёт
Мы можем создать класс
BankAccount
, где баланс (_balance
) скрыт от внешнего мира.Для доступа есть методы
deposit()
иwithdraw()
, через которые можно добавлять или снимать деньги, проверяя корректность данных (например, не позволять снять больше, чем на счёте).
class BankAccount { double _balance = 0; // приватная переменная void deposit(double amount) { if (amount > 0) { _balance += amount; } } void withdraw(double amount) { if (amount > 0 && amount <= _balance) { _balance -= amount; } } double get balance => _balance; // геттер для доступа к балансу }
Аналогия: Представьте, что это сейф с дверью. Мы не видим, что находится внутри, но можем положить или забрать что-то, используя комбинацию.
Телефон
В классе
Phone
можно скрыть сложные внутренние процессы, такие как проверка заряда батареи и соединение с сетью.При этом мы оставляем доступ к публичным методам
makeCall()
илиsendMessage()
, которые работают независимо от внутреннего устройства телефона.
class Phone { bool _isCharged = true; // приватная переменная void makeCall(String number) { if (_isCharged) { print('Звоним на $number'); } else { print('Батарея разряжена'); } } }
Машина
В классе
Car
скрываем двигатель и топливную систему, которые не видны пользователю.Публичный метод
drive()
позволяет пользователю ехать, но не требует знания о том, как работают двигатель или трансмиссия.
class Car { bool _engineRunning = false; // приватное состояние двигателя void startEngine() { _engineRunning = true; } void drive() { if (_engineRunning) { print('Машина едет'); } else { print('Сначала запустите двигатель'); } } }
Аналогия: Это как педали и руль, которые управляют машиной, не раскрывая детали того, как работает двигатель.
Робот-пылесос
В классе
RobotVacuum
можно скрыть логику передвижения и сенсоры, чтобы пользователь мог просто использовать методstartCleaning()
.
class RobotVacuum { bool _isBatteryCharged = true; void startCleaning() { if (_isBatteryCharged) { print('Начинаю уборку'); } else { print('Нужно зарядить батарею'); } } }
Аналогия: Представьте, что робот-пылесос просто "убирается", и мы не видим, как он определяет, где грязь, а где чисто.
Электронная книга
В классе
EBookReader
скрыты сложные процессы обработки и отображения текста.У пользователя есть публичные методы
openBook()
иturnPage()
, чтобы читать книгу без вмешательства в детали работы программы.
class EBookReader { String _bookContent = "Содержание книги..."; void openBook() { print("Книга открыта"); } void turnPage() { print("Переворачиваем страницу"); } }
Аналогия: Электронная книга позволяет читать текст, не раскрывая, как устройство обрабатывает и отображает данные.
Резюме
Инкапсуляция помогает:
Скрывать внутренние данные и процессы, предоставляя только нужные методы.
Защищать данные и создавать единый интерфейс, которым легко пользоваться.
Инкапсуляция делает код более надежным и удобным для работы с ним в больших проектах, особенно при поддержке и изменениях.
Пример:
Создадим мини-приложение для управления профилем пользователя. В этом приложении будет инкапсулирована информация о пользователе, такая как имя, возраст и email. Мы будем использовать инкапсуляцию, чтобы скрыть эти данные от прямого доступа и предоставить методы для их безопасного изменения и получения.
Почему использовать инкапсуляцию?
Инкапсуляция позволяет:
Защищать данные: Мы можем контролировать, как данные изменяются, например, проверять, чтобы возраст был положительным, а email соответствовал определённому формату.
Упрощать использование: Публичные методы делают взаимодействие с данными понятным и безопасным, не позволяя другим частям программы неправильно изменять внутреннее состояние объекта.
Сохранять целостность данных: Мы можем изменять внутреннюю реализацию без необходимости обновлять код, который использует эти объекты.
Структура приложения
Класс
UserProfile
— инкапсулирует данные о пользователе.Публичные методы — позволяют безопасно изменять и получать данные.
Экран профиля — демонстрирует использование класса
UserProfile
и взаимодействие с ним.
Код приложения
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: ProfileScreen(),
);
}
}
// Класс UserProfile, который инкапсулирует данные о пользователе
class UserProfile {
String _name; // Приватное поле для имени
int _age; // Приватное поле для возраста
String _email; // Приватное поле для email
UserProfile(this._name, this._age, this._email);
// Геттеры
String get name => _name;
int get age => _age;
String get email => _email;
// Сеттеры
void setName(String name) {
_name = name;
}
void setAge(int age) {
if (age > 0) { // Проверка на положительный возраст
_age = age;
} else {
print("Возраст должен быть положительным");
}
}
void setEmail(String email) {
if (RegExp(r"^[a-zA-Z0-9]+@[a-zA-Z0-9]+\.[a-zA-Z]+").hasMatch(email)) {
_email = email;
} else {
print("Неверный формат email");
}
}
}
// Экран профиля пользователя
class ProfileScreen extends StatefulWidget {
@override
_ProfileScreenState createState() => _ProfileScreenState();
}
class _ProfileScreenState extends State<ProfileScreen> {
UserProfile _userProfile = UserProfile("Иван", 30, "ivan@example.com");
final TextEditingController _nameController = TextEditingController();
final TextEditingController _ageController = TextEditingController();
final TextEditingController _emailController = TextEditingController();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("Профиль пользователя")),
body: Padding(
padding: EdgeInsets.all(16.0),
child: Column(
children: [
Text("Имя: ${_userProfile.name}"),
Text("Возраст: ${_userProfile.age}"),
Text("Email: ${_userProfile.email}"),
SizedBox(height: 20),
TextField(
controller: _nameController,
decoration: InputDecoration(labelText: "Введите новое имя"),
),
TextField(
controller: _ageController,
decoration: InputDecoration(labelText: "Введите новый возраст"),
keyboardType: TextInputType.number,
),
TextField(
controller: _emailController,
decoration: InputDecoration(labelText: "Введите новый email"),
),
SizedBox(height: 20),
ElevatedButton(
onPressed: () {
_userProfile.setName(_nameController.text);
_userProfile.setAge(int.tryParse(_ageController.text) ?? 0);
_userProfile.setEmail(_emailController.text);
setState(() {}); // Обновляем состояние, чтобы показать изменения
},
child: Text("Обновить профиль"),
),
],
),
),
);
}
}
Объяснение кода
Класс
UserProfile
:Инкапсуляция данных: Приватные переменные
_name
,_age
и_email
скрывают внутренние данные пользователя. Это предотвращает случайное или неправильное изменение этих данных.Геттеры и сеттеры: Публичные методы
get name
,get age
иget email
позволяют безопасно получать данные. МетодыsetName
,setAge
иsetEmail
предоставляют контролируемый доступ для изменения этих данных с проверкой правильности (например, положительный возраст и корректный email).
Экран профиля (
ProfileScreen
):Создаёт экземпляр
UserProfile
и предоставляет текстовые поля для ввода нового имени, возраста и email.Кнопка "Обновить профиль" вызывает сеттеры, чтобы обновить данные пользователя. Если введенные данные некорректны, это обрабатывается в сеттерах, а пользователю не нужно беспокоиться о деталях проверки.
Аналогия
Представьте, что класс UserProfile
— это прачечная. Внутри прачечной находятся стиральные машины и сушилки (приватные данные), и вы не можете просто зайти и изменить их настройки. Вместо этого есть администратор (публичные методы), который проверяет ваши запросы и управляет процессом стирки. Вы можете сказать администратору, какую одежду вам нужно постирать, но не можете напрямую изменять оборудование.
Заключение
Инкапсуляция позволяет защитить данные и контролировать их изменение, что делает код более безопасным и понятным. Мы можем менять внутренние детали класса, не нарушая взаимодействия с ним, что удобно для поддержки и расширения приложения в будущем.
Last updated