Определение
Значение термина «инкапсуляция» расплывчато и отличается от источника к источнику. Принято считать, что инкапсуляция?—?один из основополагающих принципов ООП, хотя некоторые научные статьи вовсе упускают инкапсуляцию из списка. К примеру, Джон Митчелл в книге «Концепты в языках программирования» при перечислении основных концептов в ООП упоминает только абстракцию?—?термин который принято считать близким к инкапсуляции по значению, но все-же более обширным и высокоуровневым. С другой стороны, Роберт Мартин в его книге «Чистая архитектура» явно говорит о том, что инкапсуляция, наследование и полиморфизм считается фундаментом ООП.
Разнообразие определений, данных термину «инкапсуляция», сложно привести к общему знаменателю. В целом можно выделить два подхода к значению этого термина. Инкапсуляция может быть рассмотрена как:
- связь данных с методами которые этими данными управляют;
- набор инструментов для управления доступом к данным или методам которые управляют этими данными.
Инкапсуляция как связь
Подобного рода трактовка термина «инкапсуляция» очень проста в объяснении. В данном случае, любой класс в котором есть хотя бы одна переменная и один метод который ею управляет наглядно демонстрирует этот принцип.
#!/usr/bin/python3
class Phone:
number = "111-11-11"
def print_number(self):
print( "Phone number is: ", self.number )
my_phone = Phone()
my_phone.print_number()
input( "Press Enter to exit" )
Класс “Phone” объединяет данные в переменной “number” с методом “print_number()”
Можно создать класс, который состоит только из методов (и не содержит переменных), что может быть удобно в некоторых языках программирования. Также возможно создать класс содержащий только данные, без методов, чего, во многих случаях, следует избегать. Обе практики следует применять в случае необходимости и их отношение к «объединяющей» инкапсуляции спорно.
Инкапсуляция как управление доступом
Объяснение концепции ограничения доступа к данным или методам требует гораздо большего количества деталей. Прежде всего, в этом контексте термин «доступ» следует понимать как способность видеть и / или изменять внутреннее содержимое класса. Существует несколько уровней доступа, предоставляемых большинством ООП языков. Обобщая можно сказать что данные объекта могут быть:
- публичными (
public
)?—?данные доступны всем; - приватными (
private
)?—?данные доступны только объекту/классу которому они принадлежат.
Большинство языков имеют дополнительные степени доступа, которые находятся между этими границами. К примеру, в C++ и Python3 есть три уровня доступа: публичный, защищенный и приватный; C# добавляет ключевое слово «внутренний» (internal
) в список.
Стоит отметить, что в большинстве языков программирования, уровень доступа к любым данным установлен по умолчанию. Например, в C++ по умолчанию уровень доступа к данным в классе задан как приватный—?к его данным могут обращаться только члены и друзья класса. Стандартный уровень доступа к структуре (struct
) в C++ отличается?—?он публичный, и данные в такой структуре могут быть доступны любому. Уровень доступа для переменных и методов класса в Python 3 полностью зависит от синтаксиса.
Примеры
Инкапсуляция
Python 3 предоставляет 3 уровня доступа к данным:
- публичный (
public
, нет особого синтаксиса,publicBanana
); - защищенный (
protected
, одно нижнее подчеркивание в начале названия,_protectedBanana
); - приватный (
private
, два нижних подчеркивания в начала названия,__privateBanana
).
Для краткости и простоты, только два базовых уровня (приватный и публичный) освещены в примере.
#!/usr/bin/python3
class Phone:
username = "Kate" # public variable
__how_many_times_turned_on = 0 # private variable
def call(self): # public method
print( "Ring-ring!" )
def __turn_on(self): # private method
self.__how_many_times_turned_on += 1
print( "Times was turned on: ", self.__how_many_times_turned_on )
my_phone = Phone()
my_phone.call()
print( "The username is ", my_phone.username )
# my_phone.turn_on()
# my_phone.__turn_on()
# print( “Turned on: “, my_phone.__how_many_times_turned_on)
# print( “Turned on: “, my_phone.how_many_times_turned_on)
# will produce an error
input( "Press Enter to exit" )
Доступ к публичным переменным и методам можно получить из основной программы. Попытка получить приватные данные или запустить приватный метод приведет к ошибке.
Нарушение инкапсуляции
Сам язык предоставляет программисту синтаксический инструмент, который может обойти инкапсуляцию. Читать и изменять частные переменные и вызывать частные функции все же возможно.
#!/usr/bin/python3
class Phone:
username = "Kate" # public variable
__serial_number = "11.22.33" # private variable
__how_many_times_turned_on = 0 # private variable
def call(self): # public method
print( "Ring-ring!" )
def __turn_on(self): # private method
self.__how_many_times_turned_on += 1
print( "Times was turned on: ", self.__how_many_times_turned_on )
my_phone = Phone()
my_phone._Phone__turn_on()
my_phone._Phone__serial_number = "44.55.66"
print( "New serial number is ", my_phone._Phone__serial_number )
input( "Press Enter to exit" )
Несколько слов о Магии
Существуют методы, так называемые «магические методы» («magic methods») или «специальные методы» («special methods»), которые позволяют классам определять свое поведение в отношении стандартных языковых операторов. Примером таких языковых операторов могут служить следующие выражения:
x > y
x[ i ]
Python 3 поддерживает множество таких методов, полный список можно найти на странице официальной документации языка. __init__
(инициализатор) является наиболее часто используемым из них и запускается при создании нового объекта класса. Другой, __lt__
(расширенное сравнение), определяет правила для сравнения двух объектов пользовательского класса. Такие методы не попадают в категорию «приватных» или «публичных», поскольку служат другим целям и корнями глубоко уходят во внутреннюю структуру языка.
#!/usr/bin/python3
class Phone:
def __init__(self, number): # magic method / inititalizer
print( "The Phone object was created" )
self.number = number
def __lt__(self, other): # magic method / rich comparison
return self.number < other.number
my_phone = Phone(20)
other_phone = Phone(30)
if my_phone < other_phone:
print( "Two instances of custom class were compared" )
print( "'__lt__' was called implicitly" )
if my_phone.__lt__(other_phone):
print( "Now, '__lt__' was used explicitly" )
input( "Press Enter to exit" )
Магические методы могут быть вызваны любым пользователем таким же образом как и любой публичный метод в Питоне, однако они предназначены для неявного использования в своих особых случаях. Специальный случай для метода __init__
?—?инициализация нового объекта класса. __lt__
служит для сравнения двух объектов.
Заключение
Python3 не обеспечивает ограниченный доступ к каким-либо переменным или методам класса. Данные, которые должны быть скрыты, на самом деле могут быть прочитаны и изменены. В Python3 инкапсуляция является скорее условностью, и программист должен самостоятельно заботиться о ее сохранении.
Источники
- John C. Mitchell, Concepts in programming languages
- Robert C. Martin, Clean Architecture, A Craftsman’s Guide to Software Structure and Design
Комментарии (10)
microcoder
20.03.2019 23:59Значение термина «инкапсуляция» расплывчато и отличается от источника к источнику
Почему расплывчато? Инкапсуляция от англ. encapsulation и от лат. in capsula, то есть что-то в капсуле (коробочке). Далее не трудно вообразить некую коробочку (класс), сверху которой есть только какие-то кнопки управления (публичные методы), а в целом весь механизм скрыт от пользователя (программиста который использует класс).
В данном случае, любой класс в котором есть хотя бы одна переменная и один метод который ею управляет наглядно демонстрирует этот принцип
class Phone: number = "111-11-11" def print_number(self): print( "Phone number is: ", self.number )
Мне кажется, что здесь как раз таки нет принципа инкапсуляции, поле number открыто и доступно из вне. Можно вызвать метод, а можно обратится к полю = получим один и тот же результат, стало быть ничего не сокрыто (не инкапсулировано).KaterynaBondarenko Автор
21.03.2019 01:15Почему расплывчато?
Потому как существенно отличается от источника к источнику.
Если рассматривать инкапсуляцию как сокрытие данных от пользователя — да, вы абсолютно правы, как данные так и метод открыты и доступны. Если использовать ваше определение, «группировка + сокрытие», опять таки, в этом примере данные и метод не скрыты (не инкапсулированы).
Этот пример иллюстрирует определеное значение термина «инкапсуляция» — «группировка данных и методов которые ими управляют в одно целое». В этом значении речи о сокрытии не идет, и доступ к данным не существеннен.
fireSparrow
21.03.2019 14:20+1Почему расплывчато? Инкапсуляция от англ. encapsulation и от лат. in capsula, то есть что-то в капсуле (коробочке). Далее не трудно вообразить некую коробочку (класс), сверху которой есть только какие-то кнопки управления (публичные методы)
Мне кажется, что здесь как раз таки нет принципа инкапсуляции
Если для понимания значения некоторого термина нужно привлекать фантазию, аналогии, субъективные ассоциации и аргумент «мне кажется, что», то это и значит, что термин расплывчато определён.microcoder
21.03.2019 16:24Если для понимания значения некоторого термина нужно привлекать фантазию, аналогии, субъективные ассоциации и аргумент «мне кажется, что»
Интересное заявление. С "мне кажется" согласен, это было лишнее в моём предложении, надо было более увереннее писать.
Но всё, что вы перечислили нужно исключать, чтобы получить понимание о термине? А как собственно понимать остаётся? Зубрёжкой?
В теориях получается нет терминов, так как их нельзя понимать по-вашему, так как для этого нужно привлекать всё, что вы перечислили?
термин расплывчато определён
Он не может быть в принципе быть расплывчатым или не расплывчатым. Не существует таких у него свойств. Расплывчатость лежит в границах только отдельно конкретно взятой личности и за пределы его понимания не выходит. У кого-то расплывчато, а у кого то не расплывчато. Тогда как Вы определите, расплывчат он сам по себе термин или нет?
fireSparrow
21.03.2019 16:44Все эти вещи хороши, если они помогают понять уже существующее чёткое определение. Но если кроме них ничего нет, то каждый как хочет, так и толкует.
Вот, например, ваш пример с коробочкой. Вы представили себе коробочку, увидели в своём воображении, что содержимое в ней спрятано, и пришли к выводу, что инкапсуляция — это сокрытие и ограничение доступа.
Я представил себе ту же коробочку, но увидел прежде всего то, что она — контейнер, который позволяет удобно держать в одном месте несколько вещей. И для меня инкапсуляции — это группировка логически связанных данных и методов.
Кто из нас двоих прав? А этого нельзя сказать, если у нас нет чёткого определения, а только картинка с коробочкой.
Мне кажется, что здесь как раз таки нет принципа инкапсуляции
А вот мне, исходя из моего понимания инкапсуляции, кажется, что есть.
И никаких аргументов, кроме «мне кажется» и «ну представьте себе коробочку», ни вы мне не сможете привести, ни я вам. Мы можем спорить до второго пришествия, и каждый останется при своём мнении. Ergo, термин определён крайне расплывчато.microcoder
21.03.2019 20:40Кто из нас двоих прав?
Правильного ответа не существует до тех пор, пока кто-то не получит осознание с помощью той или иной версии, он же и определит, кто прав лично для него. Для другого правда может быть другой.
Я привел пример своей версии и выразил своё мнение, что термин для меня лично не размыт, совершенно чёткий.
Мы можем спорить до второго пришествия, и каждый останется при своём мнении.
Совершенно верно. Правда у каждого своя )) На этом предлагаю закончить дискуссию и не тратить на это время )) Всего доброго!
fireSparrow
21.03.2019 21:13Термин не может быть одновременно чётко определённым и в то же время значить для каждого что-то свое. Иначе это не термин.
А то эдак мы и до альтернативной математики докатимся.
Впрочем, раз вы хотите закончить дискуссию, то и вам всего доброго ;))
AN3333
А слабо посмотреть первоисточник?
netricks
Первоисточник мало чем отличается по ценности от остальных источников. Со времен первоисточника концепция ООП улучшалась и переосмысливалась.