97 Вещей, Которые Должен Знать Каждый Программист
84. Мыслите Состояниями
У людей в реальном мире странные понятия состояний. Утром я зашёл в кофейню, чтобы подготовиться к очередному дню преобразования кофеина в код (купить латте). Но девушка ответила: "Извините, у нас вообще, совсем нет никакого молока, ни капли."
С точки зрения программиста это странное заявление. Когда дело доходит до наличия или отсутствия молока, это нельзя измерить. Молоко либо закончилось, либо нет. Возможно, она пыталась сказать мне, что у них не будет молока всю неделю, но результат был бы тот же – сегодня я пью эспрессо.
В большинстве реальных ситуаций такая вольная трактовка состояний людьми не является проблемой. Но, к сожалению, многие программисты также неряшливо обращаются с состоянием программ. А это уже проблема.
Рассмотрим простой интернет-магазин, который работает только по предоплате. Класс заказа, может содержать такой метод:
public bool IsComplete() => IsPaid() && HasShipped();Вроде логично. Но, даже если это выражение аккуратно извлечено в метод, а не продублировано повсюду, такого выражения не должно существовать вообще. Тот факт, что оно существует, говорит о проблеме. Почему? Потому что заказ не может быть отправлен до оплаты. Таким образом,
HasShipped() не может быть true, если IsPaid() не true, что делает эту часть выражения избыточной. Возможно, вам всё равно нужен метод IsComplete() для ясности кода, но тогда он должен выглядеть так:public bool IsComplete() => HasShipped();В своей работе я постоянно встречаюсь как с недостаточными, так и с избыточными проверками. Этот пример крошечный, но, если добавить сюда отмену заказа и возврат денег, он станет более сложным, и потребность в хорошей обработке состояния возрастёт. В нашем случае заказ может находиться только в одном из трёх состояний:
- В обработке: можно добавлять или удалять элементы. Нельзя отправлять.
- Оплачен: нельзя добавлять или удалять элементы. Возможна отправка.
- Отправлен: Готово. Больше никакие изменения не доступны.
Эти состояния важны, т.к. нужно убедиться, что:
1. вы находитесь в ожидаемом состоянии, прежде чем выполнять операции,
2. вы можете перейти только в одно из доступных состояний.
То есть, вы должны тщательно защитить свои объекты в нужных местах.
Но как начать мыслить состояниями? Извлечение выражений в осмысленные методы - очень хорошее начало, но это только начало. Основа - понимание конечных автоматов. Я знаю, что у вас могут возникнуть плохие воспоминания об уроках информатики, гоните их прочь. Конечные автоматы не так трудны. Визуализируйте их, чтобы сделать их простыми для понимания и обсуждения. Протестируйте свой код, чтобы выявить допустимые и недопустимые состояния и переходы и сохранить их правильность. Изучите паттерн Состояние, почитайте про программирование по контракту. Оно поможет вам обеспечить допустимое состояние, проверяя входящие данные и сам объект при входе и при выходе из каждого публичного метода.
Если ваша программа находится в неправильном состоянии, - это ошибка, и вы рискуете испортить данные, если не прервёте операцию. Если вы обнаружите, что проверки состояния вносят в код слишком много шума, изучите возможности IDE, генераторы кода или аспектное программирование (weaving), чтобы скрыть их. Независимо от того, какой подход вы выберете, мышление состояниями сделает ваш код более простым и надёжным.
Источник: https://www.oreilly.com/library/view/97-things-every/9780596809515/
Автор оригинала – Niclas Nilsson