Являясь одним из основных принципов объектно-ориентированного программирования, инкапсуляция, с моей точки зрения, все еще не совсем ясна. Казалось бы, об этом написано много статей, но, несмотря на это, я не нашел хороших (на мой взгляд) ресурсов, объясняющих этот ключевой принцип объектно-ориентированного программирования.

На высоком уровне идея принципа скрывается прямо в его названии. «Инкапсуляция» означает упаковку чего-либо в капсулу. Если говорить о программировании, то хотелось бы иметь некий «черный ящик», внутри которого была бы часть нашей бизнес-логики. Все, что нам нужно, это вызывать методы, не думая о внутренней реализации.

Дьявол прячется в деталях. Как мы можем добиться инкапсуляции в наших программах? Давайте исследуем проблему на примере простого класса BankAccount. Представьте, что вы пишете банковское приложение, которое должно отображать пользователю его деньги в разных валютах, в нашем случае это будут доллары США и евро. Как понять, инкапсулирован этот класс или нет?

Ответ состоит из двух частей. Инкапсуляция представляет собой комбинацию двух аспектов:

  1. Конфиденциальность
  2. Согласованность данных

Поговорим о них обоих подробнее.

Конфиденциальность

Конфиденциальность — это возможность скрыть некоторые детали реализации от посторонних глаз. В приведенном выше примере мы скрыли поля класса и оставили доступными только методы DepositUsd и DepositEur. Итак, пользователи нашего класса не могут написать такой код:

Компилятор вернет ошибку, так как поле _amountInUsd помечено как закрытое. Различные языки программирования реализуют конфиденциальность по-разному, например, C++, C# и Java имеют модификаторы доступа, такие как private, protected, public и т. д. Golang управляет доступом к членам уровня типа или пакета, используя соглашение об именах. Самое главное, что доступ к полям/функциям/классам/методам может быть ограничен, и никто не может изменить данные, хранящиеся в объекте класса, извне класса.

Большинство людей (на самом деле, не только джуниоры, но и разработчики с многолетним стажем) считают, что наличие средств управления доступом и есть инкапсуляция. Вернемся к нашему примеру и заполним методы DepositUsd и DepositEur:

Все его поля доступны только внутри класса, поэтому никто не может их изменить. Класс предоставляет только два общедоступных члена — методы DepositUsd и DepositEur, которые содержат логику, защищающую наши _amountInUsd и _amountInEur из отрицательных или нулевых значений.

Этот класс сейчас инкапсулирован или нет?

НЕТ. Поскольку данные в этом классе могут быть противоречивыми.

Согласованность данных

Сначала мы должны понять, что такое данные класса. Это просто значения всех полей класса. Проблема с приведенным выше кодом заключается в том, что, добавляя сумму к _amountInUsd, мы не меняем значение поля _amountInEur. Я думаю, никто не был бы счастлив, если бы они открыли свое банковское приложение и увидели сумму, которую нельзя конвертировать прямо между долларами США и евро. Это называется несогласованным состоянием данных класса, когда поля класса несовместимы.

Исправление довольно простое и очевидное. Когда мы вносим доллары США на наш счет, мы должны конвертировать поступающую сумму в евро и добавлять ее к _amountInEur. Абсолютно то же самое нужно сделать в методе DepositEur по отношению к сумме в долларах. Исправленная версия будет выглядеть так:

Теперь мы всегда знаем, что значения в наших _amountInUsd и _amountInEur согласованы, что означает, что их всегда можно правильно преобразовать из одного в другое и обратно. Пользователи класса BankAccount могут быть спокойны и использовать методы DepositUsd и DepositEur без беспокойства и необходимости погружаться в реализацию класса, что и было нашей целью. в самом начале.

Итак, только при выполнении обоих этих двух условий мы можем сказать, что наш класс инкапсулирован. Но этот принцип применим не только к классу! Инкапсуляцию можно назвать «принципом черного ящика», который можно (и, собственно, нужно) использовать для пакетов, библиотек, модулей, микросервисов, сервисов и даже целых систем, когда они интегрируются друг с другом. Надеюсь, что моя статья помогла вам лучше понять этот (возможно, самый) важный принцип объектно-ориентированного программирования!