Зарегистрируйтесь для доступа к 15+ бесплатным курсам по программированию с тренажером

Состояние объектов Java: Классы

В своей работе мы будем встречаться с двумя типами объектов, одни имеют внутреннее состояние, другие - нет. Ко вторым может, например, относиться объект, задача которого превращать формат markdown в HTML. Подобный объект используется для превращения текста этого урока написанного в формате markdown, в HTML для вывода на сайте. Вот как выглядит использование такого объекта:

// Гипотетический пример, реальные библиотеки устроены сложнее
var md = new Markdown();
// ** - означает жирность в markdown
var html = md.render("**Hexlet**");
// <b>Hexlet</b>

В этом примере метод render() никак не влияет на объект md. Метод принимает на вход данные, трансформирует их и возвращает наружу. С таким же успехом, мы могли бы сделать обычный статический метод и использовать его вместо объекта.

Markdown.render("**Hexlet**");

Зачем в таком случае нам нужен объект? Есть несколько причин одна из которых конфигурация. Преобразование markdown в HTML делается по определенным правилам, которые можно менять, например, делать из урлов html-ссылки или нет.

var md = new Markdown(/* сюда передаются опции */);

Таким образом мы можем создать несколько разных видов объектов с разной конфигурацией и затем использовать их в приложении одновременно. В каком-то смысле это тоже состояние, но это не начальное состояние объекта, которое в процессе меняется с помощью методов. Это, как правило, неизменяемое (иммутабельное) состояние, которое используется методами в процессе работы. Сами методы на объект не влияют, поэтому сюда относятся и data-классы с неизменяемым содержимым. Подобные объекты очень просты в создании и работе. С ними редко возникают какие-то сложности.

Совсем другое дело, когда речь идет про объекты с состоянием. Классический пример это пользователь. Внутри такого объекта хранятся данные, которые могут меняться в процессе жизни этого объекта.

var user = new User("Mark");
user.getName(); // Mark

user.setName("Makarello");
user.getName(); // Makarello

Точно таким же объектом с состоянием является и массив

String[] planets = {"Mars", "Jupiter", "Saturn", "Uranus", "Neptune"};
System.out.println(planets[0]); // Mars
planets[0] = "Red Planet";
System.out.println(planets[0]); // Red Planet

Чем дальше в программировании, тем больше вы будете замечать, как управление состоянием объектов становится одной из самых сложных частей приложения. Разберем несколько примеров.

Многопоточность

Эта тема изучается в самом конце, но о ней нельзя не сказать говоря о состоянии объектов. Изменение одного объекта из разных потоков (поток это единица выполнения, в одной запущенной программе может быть много потоков) может приводить к непредсказуемому результату, когда данные не соответствуют тому что ожидается. Для решения этой проблемы используются специальные механизмы синхронизации.

Конструирование и изменение

Постоянно возникает вопрос, как правильно создавать объект, передавая все данные в конструктор или заполняя их через сеттеры? Во втором случае мы можем получить объект, который прямо сейчас находится в состоянии, когда его нельзя использовать, так как он не готов. Представьте что у нас есть требование создавать пользователя с обязательным заполнением имени и email. Вот что мы можем получить.

// Объект не валиден, его нельзя использовать
var user = new User();
// Все еще не валиден
user.setEmail("support@hexlet.io");
// Вот теперь можно
user.setFirstName("Olga");

В ситуациях когда это возможно, лучше предпочитать способ с заполнением через конструктор, тогда объекты будут готовы сразу после создания.

var user = new User("Olga", "support@hexlet.io");

Целостность состояния (Инварианты)

В случае объектов, инвариантами называются правила, которым должно соответствовать внутреннее состояние объекта. Если эти правила нарушаются, значит в программе есть баги. Например, у объекта описывающего банковский счет, при снятии денег, должна быть проверка на достаточность денег на счету.

var account = new BankAccount(100.0);
// Не должно сработать, так как на счету недостаточно денег
account.withdraw(150.0);

Одна из ключевых задач программиста при проектировании классов, учитывать инварианты и реализовывать их в коде.

Зависимые объекты

На практике объекты часто хранят внутри себя ссылки на другие объекты. Что может легко приводить к нарушению инвариантов без возможности это контролировать. Представьте что у нас в коде есть сотрудник и есть компания, которую можно получить так employee.getCompany().

var company = /* Создаем или получаем объект компании */;
var employee = new Employee("Mike", company);
employee.getCompany();

Так как компания это отдельный объект, то ничто не мешает менять его напрямую без взаимодействия с employee. Причем сразу двумя способами.

// Напрямую
company.changeSomething(/* параметры */);

// Через объект employee
employee.getCompany().changeSomething(/* параметры */);

Если состояние объекта employee зависит от состояния company, то в момент таких изменений может произойти нарушение инвариантов, так как объект employee ничего не знает об изменении.


Аватары экспертов Хекслета

Остались вопросы? Задайте их в разделе «Обсуждение»

Вам ответят команда поддержки Хекслета или другие студенты

Об обучении на Хекслете

Для полного доступа к курсу нужен базовый план

Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.

Получить доступ
1000
упражнений
2000+
часов теории
3200
тестов

Открыть доступ

Курсы программирования для новичков и опытных разработчиков. Начните обучение бесплатно

  • 130 курсов, 2000+ часов теории
  • 1000 практических заданий в браузере
  • 360 000 студентов
Отправляя форму, вы принимаете «Соглашение об обработке персональных данных» и условия «Оферты», а также соглашаетесь с «Условиями использования»

Наши выпускники работают в компаниях:

Логотип компании Альфа Банк
Логотип компании Aviasales
Логотип компании Yandex
Логотип компании Tinkoff
Рекомендуемые программы
профессия
от 6 300 ₽ в месяц
Разработка приложений на языке Java
10 месяцев
с нуля
Старт 16 мая

Используйте Хекслет по-максимуму!

  • Задавайте вопросы по уроку
  • Проверяйте знания в квизах
  • Проходите практику прямо в браузере
  • Отслеживайте свой прогресс

Зарегистрируйтесь или войдите в свой аккаунт

Отправляя форму, вы принимаете «Соглашение об обработке персональных данных» и условия «Оферты», а также соглашаетесь с «Условиями использования»
Изображение Тото

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