06.08.2022

Принципы ООП. Инкапсуляция

Сергей Немчинский
7 минут просмотра
Принципы ООП. Инкапсуляция

Термин «инкапсуляция», он же первый принцип ООП, имеет две трактовки. Зачастую специалисты используют этот термин только в одном значении, забывая о другом, а это неверно.

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

Лично я понимаю под термином «инкапсуляция» обе эти трактовки – и соединение методов с данными, и сокрытие данных от внешних воздействий, изменение их только через обращение к методам самого объекта. Дело в том, что вторая трактовка без первой не работает совсем. Что вы будете скрывать, если у вас в объекте не будет методов и данных?

К чему относится принцип инкапсуляции? С точки зрения ООП, объектом является не только класс или объект, или как это называется в вашем языке программирования, но и другие структуры, более высокого уровня, а именно: packages, namespaces, модули, сабмодули, подсистемы и вся система целиком. Если рассматривать инкапсуляцию как сокрытие, мы понимаем, что наш объект не должен изменяться ничем снаружи, кроме его же методов. Это поможет избежать случайных взаимодействий типа «гайку открутили – жопа отвалилась».

Если к состоянию объекта имеет доступ только сам объект – я напоминаю, что состояние объекта это значения всех его полей – то это нам позволяет избежать лишних взаимосвязей, неожиданных сайд эффектов, когда объект работал-работал, и тут у него внезапно поменялось поле, и в нем уже совсем другое значение. Программа вылетает, и получаются прочие проблемы типа raise condition.

Для программиста важно помнить, что всегда имеет смысл скрывать все данные, закрывать все состояния объекта от внешнего объекта, и помнить о том, что сокрытие внутренней структуры вашего модуля от внешнего вмешательства, тоже имеет смысл. Обращение через интерфейс или через его внешний паблик класс – это работа с модулем через его фасад, через сервис, а не грубые работы с черного хода, чтоб посмотреть, что там внутри.

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

Из принципа инкапсуляции напрямую проистекает множество паттернов GRASP. Например, паттерн GRASP “Information expert” – это прямая имплементация паттерна инкапсуляции. Где должны обрабатываться данные? В объекте, который их содержит. Это частная, более специфическая формулировка той же самой инкапсуляции.

То же самое касается low coping. Меньше связей между объектами означает, что к объектам мы обращается только через нужные методы, а не дергаем все подряд, не используем reflection, чтоб поковыряться в кишках объекта. Только через паблик интерфейс, только через методы, изначально предназначенные для того, чтобы к ним обращаться.

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