Java – один из самых популярных языков программирования. Он обладает не только простым и понятным синтаксисом, но и позволяет создавать контент для разного рода платформ. На нем написан крупный популярный проект – игра Minecraft.
Java – это язык разработки общего назначения. Он предусматривает концепции объектно-ориентированного программирования в своем составе. Именно об этой особенности зайдет речь далее. Особое внимание будет уделено так называемой инкапсуляции. Предложенная информация пригодится как новичкам, так и тем, кто уже имел опыт в разработке программного обеспечения на Java.
Термины для ООП
Но перед тем, как подробно рассматривать инкапсуляцию, стоит запомнить некоторые ключевые термины. Они относятся не только к Java, а ко всем языкам, поддерживающим упомянутую ранее парадигму.
Чтобы не пришлось постоянно заглядывать в Google для раскрытия тех или иных определений во время написания кода, можно запомнить следующие определения:
- Алгоритм – это последовательность действий, приводящая к решению поставленной задачи.
- Переменная – именованная ячейка в памяти устройства. С ней можно выполнять разнообразные действия: хранить, считывать, корректировать, удалять.
- Оператор – объект, который умеет управлять операндами.
- Операнд – некий объект в коде, которым можно манипулировать через операторы.
- Итерация – один проход через тот или иной блок кода. Можно назвать это «одной операцией, выполненной в приложении».
- API – правила, протоколы и процедуры, при помощи которых создаются программные приложения.
- Аргумент – значение, передаваемое в функции/команды.
- Символ – элементарная единица отображения информации в коде. Равна одной буквенной записи или символу.
- Объект – некая комбинация переменных, которые связаны между собой. Сюда могут быть включены константы и иные структурные данные. Они проходят совместную выборку и обработку.
- Класс – связанные объекты, наделенные общими свойствами.
- Константа – значение, которое будет всегда оставаться неизменным.
- Тип данных – классификация информации определенного типа.
- Массив – группы и списки схожих типов значений данных. Они обязательно совместно группируются.
- Ключевое слово – слово/фраза, зарезервированное системой (языком программирования). Используется для того, чтобы обозначать разнообразные операции, команды, функции, инструменты в ЯП.
Все это можно отыскать в Google вместе с другими, более сложными понятиями при разработке программного обеспечения. Но предложенная выше информация – это «база», с которой должен ознакомиться каждый разработчик.
Новичкам об Инкапсуляции и ООП.
Отдельное внимание стоит уделить в ООП такому понятию как «метод». Он в объектно-ориентированном программировании обозначает некую функцию или процедуру, принадлежащую к классу или объекту. Метод включает в себя несколько операторов. Они необходимы для выполнения разного рода действий. Также у метода есть набор входных аргументов.
Принципы ООП. 1. Инкапсуляция
О методах
Говоря о методах в ООП и Java, стоит отметить, что они бывают, согласно Google, статическими и простыми. Первый тип не имеет доступа к информации объекта, для его применения не нужно создавать экземпляры. Второй обладает доступом к данным объекта (конкретного экземпляра соответствующего класса).
Метод отвечает за предоставление интерфейсов, при помощи которых можно получить доступ к электронным материалам object. Именно он будет отвечать за инкапсуляцию.
Существуют различные уровни доступа, предоставляемые методами:
- открытый (public string) – общий интерфейс для всех пользователей обозначенного class;
- защищенный – внутренний интерфейс для наследников;
- закрытый (private) – интерфейс, доступный исключительно в пределах заданного класса.
В Google также указано, что за счет подобной классификации можно оставлять interfaces неизменными и открытыми, но корректирую их внутреннюю реализацию. При разработке все это очень удобно.
ООП – это…
ООП (объектно-ориентированное программирование) – это специальная методология программирования, базирующаяся на представлении программ в виде совокупности некоторых объектов, которые взаимодействуют между собой. Каждый компонент будет выступать экземпляром класса. Классы складываются в общую иерархию наследования.
Google указывает на то, что ООП позволяет посмотреть на разработку не «чистой логикой и абстракциями», а в виде моделирования информационных объектов. Это дает возможность улучшить управляемость итоговым контентом.
Ключевые принципы
В объектно-ориентированном программировании используются разнообразные принципы. Google указывает на наличие:
- абстракций;
- наследования;
- полиморфизма;
- инкапсуляции.
Можно привести простой пример ООП «из обыденной жизни». Google и иные поисковики в качестве такового называют человеческую речь. Она отражает идеологию подхода к разработке, начиная с инкапсуляции представления о предмете в виде его имени и заканчивая полиморфизмом применения слова в переносном смысле. Такое явления позволяет сложить наиболее полное представление через имя объекта до понятия-класса.
Инкапсуляция – определение
Пример инкапсуляции из реальной и обыденной жизни уже был представлен. Теперь стоит рассмотреть соответствующий процесс с точки зрения разработки контента. С этим может справиться даже новичок.
Инкапсуляция, согласно Google – это процесс разделения компонентов абстракций, определяющих структуру и поведение (связь данных и методов). Необходима для того, чтобы изолировать контрактные обязательства абстракции (протоколы и интерфейсы) от их реализации.
Google указывает на то, что на деле это выглядит как разделение приложения на две части: интерфейс и непосредственную реализацию. Во втором случае обеспечивается механизм сокрытия. Он разграничивает доступ к разного рода частям компонента.
Инкапсуляция, по данным Google, характеризуется преимущественно ООП, но на деле она встречается и в других языках. В объектно-ориентированной разработке имеет связи с принципом абстракции данных. В Java обычно рассматривают этот процесс без сокрытия, как неполноценную.
Инкапсуляция – это объединение данных и методов работы с ними в «одной упаковке» («капсуле»). В качестве последней в Java будет выступать класс. Он включает в себя:
- данные – поля;
- методы – используются для работы с соответствующей информацией.
В ООП каждая программа инкапсулирована: состоит из классов-капсул, которые одновременно выполняют роль и данных, и методов.
Сокрытие
В Google указано, что при рассмотрении соответствующей темы, целесообразно говорить о процессе под названием «сокрытие». Суть данного процесса заключается в том, что все «внутренности» утилиты будут скрываться от глаз обычного пользователя. Информация о том, «как все работает», не играет для среднестатистического клиента никакой роли.
Вот пример с классом Auto:
А вот – сокрытие данных в Java:
Во втором случае основные помощники программиста это:
- модификаторы доступа (они имеют прямое отношение к методам);
- геттеры;
- сеттеры.
Все перечисленные компоненты пригодятся для получения на выходе качественного программного обеспечения.
Особенности
Если инкапсулировать приложения, удастся получить несколько преимуществ. В Google указаны следующие сильные стороны процедуры под названием encapsulation:
- Контроль за грамотным поведением объекта. Здесь огромную роль играют методы и уровни доступа.
- Составление удобного для пользователя интерфейса. «Снаружи» остается лишь важная для человека информация.
- Изменения в исходном коде никак не отражаются на пользователе. Все корректировки осуществляются внутри методов. А за них отвечает разработчик.
Инкапсуляция – это программирование «капсулами». Здесь удастся увидеть еще один пример соответствующей операции, но уже на C++. А быстрее освоить методологию помогут специализированные компьютерные курсы. Их в Google полно, как и сопутствующих материалов.
Источник: otus.ru
Пример инкапсуляции в программе
Скрытие информации в Java
Как мы уже говорили, вы также можете использовать концепцию инкапсуляции для реализации механизма сокрытия информации. Этот подход, как и абстракция, один из наиболее часто используемых механизмов в Java. Его примеры можно найти почти во всех хорошо реализованных Java-классах. Механизм сокрытия делает атрибуты класса недоступными извне.
Больше полезных материалов вы найдете на нашем телеграм-канале «Библиотека джависта»
Модификаторы доступа
Говоря об этой концепции, не лишним будет разобрать инструменты для обозначения доступности элементов – модификаторы. Java поддерживает четыре модификатора доступа, используемые для определения видимости классов, методов и атрибутов. Каждый из них указывает уровень доступности, и вы можете использовать только один для каждого класса, метода или атрибута. Перечислим их, начиная с наиболее ограничивающих и заканчивая наименее строгими:
- private.
- no modifier.
- protected.
- public.
Рассмотрим более детально каждый из них и поговорим, когда вам следует их использовать.
Модификатор private
Самый ограничивающий и наиболее часто используемый модификатор доступа private делает атрибут или метод доступным только в пределах одного и того же класса. Подклассы или любые другие классы в том же или другом пакете не могут получить доступ к этому атрибуту или методу. Используем его только для атрибутов и методов, которые мы больше никогда не захотим вызвать.
В нашем примере мы используем его, чтобы ограничить доступ ко всем атрибутам и методам brewEspresso и brewFilter . Эти атрибуты и методы должны использоваться только в классе Machine и не являются частью общедоступного API. Вначале это может показаться немного запутанным. Однако очень полезно, когда классы в вашем пакете реализуют четко определенный набор логики. Это также удобно, если вы хотите управлять API, доступным для классов за пределами этого пакета. Впоследствии вы можете использовать видимость пакета для реализации метода, используемого только классами в этом пакете.
Модификатор no modifier
Отсутствие модификатора означает, что вы можете получить доступ к атрибутам и методам внутри вашего класса и из всех классов в одном пакете. Вот почему его часто называют пакетным.
Модификатор protected
Атрибуты и методы с модификатором доступа protected могут быть доступны внутри вашего класса всеми классами в одном пакете, а также всеми подклассами в том же или других пакетах. Этот модификатор, как правило, используется для внутренних методов, которые должны вызываться или переопределяться подклассами. Вы также можете использовать его, чтобы разрешить подклассам прямой доступ к внутренним атрибутам суперкласса.
Модификатор public
Доступ к методам и атрибутам, использующим модификатор public , можно получить как в текущем классе, так и во всех других классах.
Публичные методы и атрибуты становятся частью общедоступного API вашего класса и любого компонента, в котором вы их используете. Когда метод общедоступен, вам необходимо убедиться, что он хорошо задокументирован и надежно обрабатывает любые входные значения. Также имейте в виду, что этот метод будет использоваться какой-то частью вашего приложения, что затруднит его изменение или удаление.
Как правило, ваш общедоступный API должен быть максимально компактным и должен включать только методы, предназначенные для других частей вашего приложения или доступа внешних клиентов.
В нашем примере класс Machine должен быть общедоступным, поскольку он представляет интерфейс кофемашины и предназначен для использования другими классами, которые не обязательно должны быть частью того же пакета. Конструктор и методы brewCoffee и addBeans доступны другим классам для создания нового экземпляра Machine , когда вы добавляете в автомат кофейные зерна или завариваете свежую чашку кофе.
Пример 2
Здесь наглядно демонстрируется механизм сокрытия информации, описывающей напиток, приготовленный с помощью нашей кофемашины.
public class Coffee < private CoffeeSelection selection; private double quantity; public Coffee (CoffeeSelection selection, double quantity) < this.selection = selection; this.quantity = quantity; >public CoffeeSelection getSelection() < return selection; >public double getQuantity() < return quantity; >public void setQuantity(double quantity) throws CoffeeException < if (quantity >= 0.0) < this.quantity = quantity; >else < throw new CoffeeException(«Количество должно быть >= 0.0»); > > >
Класс Coffee использует два закрытых атрибута для хранения информации: CoffeeSelection и quantity . Модификатор доступа private делает оба атрибута недоступными для других классов в том же или других пакетах. Если вы хотите получить информацию о текущем состоянии объекта, вы можете вызвать один из общедоступных методов.
Метод getSelection обеспечивает доступ для чтения к атрибуту selection . Он представляет тип кофе, приготовленный кофемашиной. Как видно из фрагмента кода, мы не реализовали метод set для этого атрибута, поскольку мы не можем изменить сорт кофе после того, как он заварен. Доступное количество напитка меняется со временем. После каждого глотка в вашей чашке остается немного меньше. Поэтому мы реализовали метод получения и установки для атрибута quantity .
Если вы внимательно посмотрите на метод setQuantity , то увидите, что мы также реализовали дополнительную проверку. Если кофе особенно вкусный, вы можете пить его, пока ваша чашка не опустеет. Когда вы это сделаете, ваш кофе закончится, и вы больше не сможете его пить. Таким образом, количество кофе должно быть больше или равно нулю.
Инкапсуляция описывает объединение данных и методов, работающих с этими данными, в один модуль и используется для реализации механизма сокрытия информации. Это концепция ООП помогает нам защитить пользовательскую информацию от ошибочных действий, тем самым повышая эффективность дальнейшей работы с кодом.
Материалы по теме
- ☕ Учебник по Java: cтатический и динамический полиморфизм
- ☕ Разбираем на простых примерах: наследование в Java
Источник: proglib.io
Определение
Инкапсуляция это набор инструментов для управления доступом к данным или методам которые управляют этими данными. С детальным определением термина “инкапсуляция” можно ознакомиться в моей предыдущей публикации на Хабре по этой ссылке. Эта статья сфокусирована на примерах инкапсуляции в Си++ и Си.
Инкапсуляция в Си++
По умолчанию, в классе ( class ) данные и методы приватные ( private ); они могут быть прочитаны и изменены только классом к которому принадлежат. Уровень доступа может быть изменен при помощи соответствующих ключевых слов которые предоставляет Си++.
В Си++ доступно несколько спецификаторов, и они изменяют доступ к данным следующим образом:
- публичные ( public ) данные — доступны всем;
- защищенные ( protected ) — доступны только классу и дочерним классам;
- приватные ( private ) —доступны только классу которому они принадлежат.
Для краткости, только два уровня (приватный и публичный) будут освещены в примерах.
Пример инкапсуляции
В классе Contact , публичные переменные и методы доступны из основной программы ( main ). Приватные переменные и методы могут прочитаны, вызваны или изменены только самим классом.
#include using namespace std; class Contact < private: int mobile_number; // private variable int home_number; // private variable public: Contact() // constructor < mobile_number = 12345678; home_number = 87654321; >void print_numbers() < cout >; int main() < Contact Tony; Tony.print_numbers(); // cout
Попытка напечатать или изменить приватную переменную mobile_number из основной программы ( main ) вызовет ошибку при компиляции потому как доступ к приватным данным в классе ограничен.
Нарушение инкапсуляции с Друзьями (Хорошая практика)
В Си++ присутствует ключевое слово “друг” ( friend ) которое позволяет добавить исключения в общие правила доступа к данным. Если функция или класс названы другом ( friend ) класса Contact — они получают свободный доступ к защищенным или приватным данным.
Существует два основных правила дружбы — дружба не наследуется и не взаимна. Также, наличие “друзей” не изменяет уровень защищенности данных — приватные данные остаются приватными с исключением в виде “друга”.
#include using namespace std; class Contact < private: int mobile_number; // private variable int home_number; // private variable public: Contact() // constructor < mobile_number = 12345678; home_number = 87654321; >// Declaring a global ‘friend’ function friend void print_numbers( Contact some_contact ); >; void print_numbers( Contact some_contact ) < cout int main()
В этом примере, функция print_numbers() — обычная функция, не метод класса Contact . Объявление функции print_numbers() “другом” класса Contact — единственная причина по которой функция print_numbers() имеет доступ к приватным данным. Если убрать строку с определением друга — код не скомпилируется.
Примечание: друзьями лучше не злоупотреблять. Добавление друга стоит рассматривать как исключение, не как общую практику.
Нарушение инкапсуляции с Преобразованием типов и Указателями (Плохая практика)
Прежде всего, стоит заметить что использовать указатели и преобразование типов таким способом — плохая идея. Этот способ не гарантирует получения нужных данных. Он плохо читается и плохо поддерживается. Невзирая на это, он существует.
Си++ получил в наследство от Си множество инструментов, один из которых — преобразование типов ( typecasting ). По умолчанию, все переменные и методы в классе приватные. В то же время, стандартный уровень доступа к данным в структуре ( struct ) — публичный. Возможно создать структуру или полностью публичный класс в котором данные будут расположены идентично данным в классе Contact и используя преобразование типов получить доступ к приватным данным.
#include using namespace std; class Contact < private: int mobile_number; // private variable int home_number; // private variable public: Contact() // constructor < mobile_number = 12345678; home_number = 87654321; >void print_numbers() < cout >; struct Contact_struct < int mobile_number; int home_number; >; int main() < Contact Tony; Contact_struct * structured_Tony; Tony.print_numbers(); structured_Tony = (Contact_struct *) structured_Tony->mobile_number = 20; structured_Tony->home_number = 30; Tony.print_numbers(); return 0; >
Приватные данные были прочитаны и изменены благодаря преобразованию типов
Инкапсуляция в Си
Традиционно считается что инкапсуляция — один из ключевых ООП принципов. Тем не менее, это не лимитирует использование этого принципа в процедурно-ориентированных языках. В Си, инкапсуляция используется давно, невзирая на отсутствие ключевых слов “приватный” и “публичный”.
Приватные переменные
В контексте инкапсуляции, все данные в Си могут быть рассмотрены как публичные по умолчанию. Уровень доступа к переменным в структурах ( struct ) может быть изменен на приватный если изолировать их определение от основной программы. Нужный эффект может быть достигнут при использовании отдельных заголовочных (header, .h) и исходных (source, .c) файлов.
В данном примере, структура была определена в отдельном исходном файле “private_var.c”. Поскольку инициализация структуры в Си требует выделения и освобождения памяти, несколько вспомогательных функций были добавлены.
#include «private_var.h» #include #include struct Contact < int mobile_number; int home_number; >; struct Contact * create_contact() < struct Contact * some_contact; some_contact = malloc(sizeof(struct Contact)); some_contact->mobile_number = 12345678; some_contact->home_number = 87654321; return( some_contact ); > void delete_contact( struct Contact * some_contact )
#ifndef PRIVATE_VAR #define PRIVATE_VAR struct Contact; struct Contact * create_contact(); void delete_contact( struct Contact * some_contact ); #endif /* PRIVATE_VAR */
#include «private_var.h» #include int main() < struct Contact * Tony; Tony = create_contact(); // printf( «Mobile number: %dn», Tony->mobile_number); // will cause compile time error delete_contact( Tony ); return 0; >
Получение доступа к приватным переменным с Указателями
Преобразование типов может быть использовано для преодоления инкапсуляции в Си также как и в Си++, но данный подход уже был описан. Зная, что в структуре данные расположены в порядке их декларации, указатели и арифметика указателей подойдет для достижения цели.
Доступ к переменным в структуре ограничен. Тем не менее, спрятаны только переменные, не память в которой хранятся данные. Указатели можно рассматривать как ссылку на адрес памяти, и если эта память доступна программе — данные сохраненные в этой памяти можно прочитать и изменить. Если указатель назначен на память в которой структура хранит свои данные — их можно прочитать. Используя то же определение структуры (те же “.c” и “.h” файлы) и модифицированный “main.c” файл, ограничение доступа было преодолено.
#include «private_var.h» #include int main()
Данные в структуре были прочитаны и модифицированы
Приватные функции
Функции, будучи внешними ( extern ) по умолчанию, видимы во всей так называемой единице трансляции ( translation unit ). Другими словами, если несколько файлов скомпилированы вместе в один объектный файл, любой из этих файлов сможет получить доступ к любой функции из любого другого файла. Использование ключевого слова “статический” ( static ) при создании функции ограничит ее видимость до файла в котором она была определена.Следовательно, для обеспечения приватности функции необходимо выполнить несколько шагов:
- функция должна быть объявлена статической ( static ) либо в исходном файле (.c), либо в соответствующем заголовочном файле (.h);
- определение функции должно находиться в отдельном исходном файле.
В данном примере, в файле “private_funct.c”, была определена статическая функция print_numbers() . К слову, функция delete_contact() успешно вызывает print_numbers() поскольку они находятся в одном файле.
#include «private_funct.h» #include #include struct Contact < int mobile_number; int home_number; >; struct Contact * create_contact() < struct Contact * some_contact; some_contact = malloc(sizeof(struct Contact)); some_contact->mobile_number = 12345678; some_contact->home_number = 87654321; return( some_contact ); > static void print_numbers( struct Contact * some_contact ) < printf(«Mobile number: %d, «, some_contact->mobile_number); printf(«home number = %dn», some_contact->home_number); > void delete_contact( struct Contact * some_contact )
В соответствующем заголовочном файле «private_funct.h», print_numbers() была декларирована как статическая функция.
#ifndef PRIVATE_FUNCT_H #define PRIVATE_FUNCT_H struct Contact; struct Contact * create_contact(); static void print_numbers( struct Contact * some_contact ); void delete_contact( struct Contact * my_points ); #endif /* PRIVATE_FUNCT_H */
Основная программа, “main.c”, успешно вызывает print_numbers() опосредовательно через delete_contact() , поскольку обе функции находятся в одном документе. Тем не менее, попытка вызвать print_numbers() из основной программы вызовет ошибку.
#include «private_funct.h» #include int main() < struct Contact * Tony; Tony = create_contact(); // print_numbers( Tony ); // will cause compile time error delete_contact( Tony ); return 0; >
Получение доступа к приватным функциям
Вызвать функцию print_numbers() из основной программы возможно. Для этого можно использовать ключевое слово goto или передавать в main указатель на приватную функцию.
Оба способа требуют изменений либо в исходном файле “private_funct.c”, либо непосредственно в теле самой функции. Поскольку эти методы не обходят инкапсуляцию а отменяют её, они выходят за рамки этой статьи.
Заключение
Инкапсуляция существует за пределами ООП языков. Современные ООП языки делают использование инкапсуляции удобным и естественным. Существует множество способов обойти инкапсуляцию и избежание сомнительных практик поможет ее сохранить как в Си, так и в Си++.
- инкапсуляция
- oop concepts
- encapsulation
Источник: habr.com