Development

Инкапсулируйте не только состояние, но и поведение

(В оригинале – Encapsulate Behavior, not Just State)

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

Модули и пакеты инкапсулируют большие объемы и системы, а классы и функции – более тонкие аспекты. В течении многих лет я сталкиваюсь с тем, что именно классы являются самым проблемным местом для правильного использования инкапсуляции. Весьма часто можно увидеть класс, в котором присутствует единственный метод длиной в 3000 строк, или же класс, в котором реализованы лишь set() и get() методы для каждого атрибута. Эти примеры показывают, что разработчики не до конца понимают объектно-ориентированную модель, теряя возможность использовать всю ее мощь.

Объект инкапсулирует и состояние, и поведение, причем поведение зависит от актуального состояния. Представьте себе объект «Дверь». У него будет четыре состояния: «Открыто», «Закрыто», «Открывается», «Закрывается». И у него будет две операции: «Открыть» и «Закрыть». В зависимости от состояния, методы «Открыть» и «Закрыть» будут работать по-разному.

Такое присущее объекту свойство делает процесс проектирования относительно простым. Он сжимается до двух простых задач:
назначение и делегирование ответственности от объекта к объекту, включая протокол взаимодействия объектов.

То, как это работает на практике, проще всего показать на примере. Пусть, например, у нас есть три класса: Customer, Order и Item. Объект Customer – естественное место для инкапсулирования кредитного лимита и правила его проверки. Объект Order знает об ассоциированном Customer, и его метод addItem делегирует саму проверку ему, вызывая customer.validateCredit(item.price()) . Если условие не выполняется, генерируется исключение и покупка не выполняется.

Менее опытный ООП-разработчик может сделать по-другому – собрать все бизнес-правила в отдельный объект OrderManager (или OrderService ). В таком дизайне Order , Customer , и Item рассматриваются лишь как набор данных. Вся логика же вынесена из них и сосредоточена в большом методе класса OrderManager , с большим количеством ветвей и условий внутри. Такие методы очень легко «сломать» и почти невозможно поддерживать. Причина? Нарушенная инкапсуляция.

Подведя итог: не нарушайте инкапсуляции и используйте свойства языка для ее поддержки.

Автор оригинала – Einar Landre