Инкапсуляция

  • Рубрика записи:ООП

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

Как ни странно, в этом понятии люди путаются чаще всего. Недостаточно сказать, что “инкапсуляция – это когда все поля приватные!”.

Что такое инкапсуляция и зачем

Когда вы включаете ноутбук, вам не нужно думать об инициализации BIOS или UEFI, о поиске жёсткого диска или SSD, о загрузке ОС, драйверов и модулей в оперативку и о запуске служб.

Когда вы нажимаете на клавишу на клавиатуре, вы можете не париться о замыкании электрического контакта внутри клавишницы и о передаче этого сигнала в ОС.

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

Инкапсуляция с английского – encapsulation – буквально “помещение в капсулу”, то есть в оболочку. Оболочка означает, что внутренности остаются внутри. Наружу ничего из внутреннего не торчит.

В каждом классе присутствуют 2 аспекта: интерфейс и реализация.

Причём интерфейс не в понятии public interface …, а именно как способ взаимодействия с классом. Какие публичные методы могут вызывать другие классы в этом классе. Что видит тот, кто использует этот класс.

Реализация же – напротив, приватные поля и приватные методы, которые класс использует только внутри себя. Никто снаружи о них не в курсе. И не нужно: пользователи этого класса смогут взаимодействовать с ним, не заботясь о его внутренней реализации. Для них это будет гораздо проще: они могут не париться, реализовали вы хранение данных в массиве или двусвязном списке и как вы производите поиск объекта. Всё, что их волнует – публичные методы, которые они могут вызвать. Публичные методы не раскрывают внутренней реализации.

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

Инкапсуляция скрывает подробности реализацииобъединяя внутренние параметры (поля) объекта с его поведением (методы).

Реализация инкапсуляции в Java

В Java инкапсуляция реализуется с использованием модификаторов доступа. Они определяют область видимости полей и методов класса. Язык Java предоставляет четыре уровня доступа:

  1. public, элементы с этим модификатором доступа доступны из любого места, как внутри, так и вне пакета.
  2. protected, элементы с этим модификатором доступа доступны только внутри пакета и для подклассов (наследников) данного класса.
  3. default (package-private), если не указан модификатор доступа, то элемент доступен только внутри пакета.
  4. private, элементы с этим модификатором доступа доступны только внутри класса, в котором они были объявлены.
Таблица с модификаторами доступа Java: public, protected, package-private и private

Модификатор доступа пишется перед переменной или методом в классе:

Хороший тон – объявлять все поля класса private. Часть методов будет public, остальные – private. Публичными будут те методы, которые будут вызываться извне. Они будут втихую шебуршать внутренними данными, а потом наружу возвращать только то, что можно трогать пользователю.

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

Примеры кода

Представьте, что вы – фермер и вы ухаживаете за лошадьми. Точнее, вы играете в подобный симулятор. У каждой лошадки будет своё состояние и поведение. Состояние описывается 4 скрытыми от внешних глаз характеристиками: сытость, чистота, энергия и настроение. В свою очередь, общедоступное поведение будет описываться тем, что лошадь может делать: поспать, поесть, помыться, поскакать и потренироваться (трюки там всякие). Также будет поведение, которое нельзя вызвать извне: ржание лошади. Разве что она будет ворчать при попытке потренироваться или поскакать при низком уровне энергии. Нарисуем UML-диаграмму класса, представляющего лошадь:

UML-диаграмма класса Horse, представляющего лошадь с инкапсуляцией своего состояния

Как видно, у класса Horse 4 поля (все поля приватные) и 5 методов. 4 из них публичные, а один приватный. Суть в том, что извне вы не сможете изменить состояние лошади. Просто взять и назначить ей 10 сытости и 5 энергии. Также вы не можете заставить лошадку ржать. Вы сможете только вызвать публичные методы sleep(), eat(), wash(), ride() и train(). А состояние у лошадки будет регулироваться само посредством заданных внутри операций и вычислений. Эта логика и эти данные инкапсулированы в ней.

Вот, как этот класс можно представить в коде на Java:

Теперь снаружи, создавая объект Horse, вы можете получить доступ только к публичным методам: eat(), ride(), sleep(), train(), wash().

Вы не сможете напрямую получить доступ к private-полям, таким как energy, satiety, purity, mood. Также вы не сможете вызвать private-метод neigh(). Лошадь будет сама решать, когда ей вызвать neigh().

Даже если суть инкапсуляции уже понятна, давайте поиграемся с классом Horse. Не просто же так написали!

Чтобы отслеживать состояние нашей лошадки, введём в класс Horse переопределение метода toString():

Отлично, теперь будет в классе Main вызывать публичные методы лошади и следить за её состоянием, выводя его в консоль.

Что мы видим в итоге? Изначально у лошади все показатели по 10. После eat() они немного изменились, после ride() и wash() ещё больше. Что интересно, так это то, что после wash() у лошади осталось 0 энергии. Поэтому, когда мы попробовали вызвать train(), она отказалась тренироваться, вызвав neigh() и написав “Игого!”. Ну, а после здорового крепкого сна посредством sleep() лошадь восстановила свою энергию. Думаю, после этого она уже была бы готова к хорошей тренировке.

Вывод в консоль состояния объекта класса Horse, который инкапсулирует состояние лошади

Так что, ребята, инкапсуляция – то, без чего ООП в принципе невозможно. Если не предоставлять объектам возможность самим управлять собой, если позволять другим лезть в дела этих объектов – всё упадёт.

Добавить комментарий