Приватность на уровне библиотек
Часть раздела Объектно-ориентированное программирование путешествия по Dart на Coddy — урок 32 из 110.
В Dart приватность обеспечивается на уровне библиотеки, а не на уровне класса. Это ключевое отличие от таких языков, как Java или C++. Библиотека в Dart — это обычно один файл или несколько файлов, соединенных директивами part.
Приватные члены (начинающиеся с _) доступны в любом месте той же библиотеки, даже из других классов:
// Оба класса в одном файле (одна библиотека)
class Engine {
int _horsepower = 200; // Приватно для этой библиотеки
}
class Car {
void showPower(Engine engine) {
print(engine._horsepower); // Доступно! Та же библиотека
}
}Однако, когда вы импортируете класс из другого файла, его приватные члены становятся по-настоящему скрытыми:
// файл: engine.dart
class Engine {
int _horsepower = 200;
int get power => _horsepower;
}
// файл: main.dart
import 'engine.dart';
void main() {
var engine = Engine();
// print(engine._horsepower); // Ошибка! Нет доступа
print(engine.power); // Работает — используется публичный геттер
}Эта приватность на уровне библиотеки означает, что классы в одном и том же файле могут тесно взаимодействовать, при этом скрывая детали реализации от внешнего кода. Это осознанное проектное решение, которое стимулирует организацию связанных классов вместе в одной библиотеке.
Задание
ЛегкоДавайте создадим систему командного взаимодействия, которая демонстрирует, как работает приватность на уровне библиотеки в Dart. Вы увидите, как классы в одном и том же файле могут получать доступ к приватным членам друг друга, в то время как код в отдельных файлах этого сделать не может.
Вы организуете свой код в двух файлах:
team.dart: Этот файл будет содержать два класса, которые тесно взаимодействуют в одной библиотеке:- Класс
Memberс публичным полемString nameи приватным полемint _performanceScore, инициализированным значением50. Добавьте конструктор, принимающий имя, и публичный геттерperformanceScore, который возвращает приватный показатель. - Класс
TeamLeadв том же файле, который может напрямую обращаться к приватному полюMember(поскольку они находятся в одной библиотеке). Этот класс должен иметь публичное полеString name, конструктор, принимающий имя, и методevaluateMember(Member member), который напрямую обращается кmember._performanceScore, добавляет к нему10и выводит[leadName] boosted [memberName]'s score to [newScore].
- Класс
main.dart: Импортируйте файл вашей команды и продемонстрируйте разницу в доступе:- Создайте
Memberс именем'Alice' - Создайте
TeamLeadс именем'Bob' - Выведите
Initial score: [score], используя публичный геттер у Alice - Пусть Bob оценит Alice с помощью
evaluateMember - Выведите
Updated score: [score], используя публичный геттер - Выведите
Direct access from main: Not allowed(это напоминает нам о том, чтоmain.dartне может напрямую обращаться к_performanceScore, так как находится в другой библиотеке)
- Создайте
Ключевой момент здесь заключается в том, что TeamLead может напрямую читать и изменять приватное поле _performanceScore класса Member, потому что оба класса находятся в одном файле (одной библиотеке). Однако код в main.dart должен использовать публичный геттер, так как он находится в другой библиотеке.
Ожидаемый вывод:
Initial score: 50
Bob boosted Alice's score to 60
Updated score: 60
Direct access from main: Not allowedШпаргалка
В Dart инкапсуляция (приватность) обеспечивается на уровне библиотеки, а не на уровне класса. Библиотека — это обычно один файл или несколько файлов, соединенных директивами part.
Приватные члены начинаются с символа подчеркивания (_) и доступны в любом месте той же библиотеки, даже из других классов:
// Оба класса в одном файле (одна библиотека)
class Engine {
int _horsepower = 200; // Приватно для этой библиотеки
}
class Car {
void showPower(Engine engine) {
print(engine._horsepower); // Доступно! Та же библиотека
}
}Когда вы импортируете класс из другого файла, его приватные члены становятся скрытыми:
// файл: engine.dart
class Engine {
int _horsepower = 200;
int get power => _horsepower;
}
// файл: main.dart
import 'engine.dart';
void main() {
var engine = Engine();
// print(engine._horsepower); // Ошибка! Недоступно
print(engine.power); // Работает — используется публичный геттер
}Такой подход поощряет группировку связанных классов в одной библиотеке, позволяя им тесно взаимодействовать, скрывая при этом детали реализации от внешнего кода.
Попробуйте сами
import 'team.dart';
void main() {
// TODO: Создайте Member с именем 'Alice'
// TODO: Создайте TeamLead с именем 'Bob'
// TODO: Выведите "Initial score: [score]", используя публичный getter для Alice
// TODO: Пусть Bob оценит Alice, используя evaluateMember
// TODO: Выведите "Updated score: [score]", используя публичный getter
// TODO: Выведите "Direct access from main: Not allowed"
// Note: Мы не можем получить доступ к alice._performanceScore здесь, потому что
// main.dart находится в другой библиотеке, чем team.dart
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
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
Паттерн КомандаПаттерн АдаптерПаттерн ДекораторПаттерн Шаблонный методПаттерн СостояниеПаттерн КомпоновщикПаттерн Репозиторий