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

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

Модули и пакеты решают задачи инкапсуляции крупного масштаба, в то время как классы, подпрограммы и функции призваны решать те же задачи на более низком уровне. За годы работы я обнаружил, что из всех видов инкапсуляции тяжелее всего программистам дается инкапсуляция в классах. Нередко встречается класс, единственный метод main которого имеет 3000 строк, или же класс, в котором есть только методы set и get для его элементарных атрибутов. Такие примеры показывают, что разработчики этих классов не вполне освоили объектно-ориентированное мышление и не умеют воспользоваться мощью объектов как моделирующих конструкций. Для тех, кто знаком с терминами POJO (Plain Old Java Object — простой Java-объект в старом стиле) и POCO (Plain Old C# Object или Plain Old CLR Object), замечу: изначально они выражали возврат к основам ООП как парадигмы моделирования — понятным и простым, но не тупым объектам.

Объект инкапсулирует как состояние, так и поведение, причем поведение определяется фактическим состоянием. Возьмем объект «дверь». У него четыре состояния: открыта, закрыта, открывается, закрывается. Он предоставляет две операции: «открыть», «закрыть». В зависимости от состояния операции «открыть» и «закрыть» ведут себя по-разному. Это неотъемлемое свойство объекта делает процесс проектирования концептуально простым. Все сводится к двум простым задачам: назначению и делегированию обязанностей различных объектов, включая и протоколы межобъектного взаимодействия.

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

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

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

Загрузка...