Инкапсулируйте не только состояние, но и поведение
(В оригинале - 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