Что такое пакет в программировании
Перейти к содержимому

Что такое пакет в программировании

  • автор:

Учебники. Программирование для начинающих.

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

Programm.ws — это сайт, на котором вы можете почитать литературу по языкам программирования , а так-же посмотреть примеры работающих программ на С++, ассемблере, паскале и много другого..

Программирование — в обычном понимании, это процесс создания компьютерных программ.
В узком смысле (так называемое кодирование) под программированием понимается написание инструкций — программ — на конкретном языке программирования (часто по уже имеющемуся алгоритму — плану, методу решения поставленной задачи). Соответственно, люди, которые этим занимаются, называются программистами (на профессиональном жаргоне — кодерами), а те, кто разрабатывает алгоритмы — алгоритмистами, специалистами предметной области, математиками.
В более широком смысле под программированием понимают весь спектр деятельности, связанный с созданием и поддержанием в рабочем состоянии программ — программного обеспечения ЭВМ. Более точен современный термин — «программная инженерия» (также иначе «инженерия ПО»). Сюда входят анализ и постановка задачи, проектирование программы, построение алгоритмов, разработка структур данных, написание текстов программ, отладка и тестирование программы (испытания программы), документирование, настройка (конфигурирование), доработка и сопровождение.

Cамоучитель по Java

Глава 3. Пакеты и интерфейсы

Пакет и подпакет

В стандартную библиотеку Java API входят сотни классов. Каждый программист в ходе работы добавляет к ним десятки своих. Множество классов становится необозримым. Уже давно принять классы объединять в библиотеки. Но библиотеки классов, кроме стандартной, не являются частью языка.

Разработчики Java включили в язык дополнительную конструкцию — паке-ты (packages). Все классы Java распределяются по пакетам. Кроме классов пакеты могут включать в себя интерфейсы и вложенные подпакеты (subpackages). Образуется древовидная структура пакетов и подпакедчэв.

Эта структура в точности отображается на структуру файловой системы. Все файлы с расширением class (содержащие байт-коды), образующие пакет, хранятся в одном каталоге файловой системы. Подпакеты собраны в подкаталоги этого каталога.

Каждый пакет образует одно пространство имен (namespace). Это означает, что все имена классов, интерфейсов и подпакетов в пакете должны быть уникальны. Имена в разных пакетах могут совпадать, но это будут разные программные единицы. Таким образом, ни один класс, интерфейс или под-пакет не может оказаться сразу в двух пакетах. Если надо использовать два класса с одинаковыми именами из разных пакетов, то имя класса уточняется именем пакета: пакет.класс . Такое уточненное имя называется полным именем класса (fully qualified name).

Все эти правила, опять-таки, совпадают с правилами хранения файлов и подкаталогов в каталогах.

Пакетами пользуются еще и для того, чтобы добавить к уже имеющимся правам доступа к членам класса private, protected и public еще один, «пакетный» уровень доступа.

Если член класса не отмечен ни одним из модификаторов private, protected, public, то, по умолчанию, к нему осуществляется пакетный доступ (default access), а именно, к такому члену может обратиться любой метод любого класса из того же пакета. Пакеты ограничивают и доступ к классу целиком — если класс не помечен модификатором public , то все его члены, даже открытые, public , не будут видны из других пакетов.

Как же создать пакет и разместить в нем классы и подпакеты?

Пакет и подпакет

Чтобы создать пакет надо просто в первой строке Java-файла с исходным кодом записать строку package имя; , например:

Тем самым создается пакет с указанным именем mypack и все классы, записанные в этом файле, попадут в пакет mypack . Повторяя эту строку в начале каждого исходного файла, включаем в пакет новые классы.

Имя подпакета уточняется именем пакета. Чтобы создать подпакет с именем, например, subpack , следует в первой строке исходного файла написать;

и все классы этого файла и всех файлов с такой же первой строкой попадут в подпакет subpack пакета mypack .

Можно создать и подпакет подпакета, написав что-нибудь вроде

и т. д. сколько угодно раз.

Поскольку строка packageимя; только одна и это обязательно первая строка файла, каждый класс попадает только в один пакет или подпакет.

Компилятор Java может сам создать каталог с тем же именем mypack, a в нем подкаталог subpack, и разместить в них class-файлы с байт-кодами.

Полные имена классов А, в будут выглядеть так: mypack.A, mypack.subpack.в.

Фирма SUN рекомендует записывать имена пакетов строчными буквами, тогда они не будут совпадать с именами классов, которые, по соглашению, начинаются с прописной. Кроме того, фирма SUN советует использовать в качестве имени пакета или подпакета доменное имя своего сайта, записанное в обратном порядке, например:

До сих пор мы ни разу не создавали пакет. Куда же попадали наши файлы с откомпилированными классами?

Компилятор всегда создает для таких классов безымянный пакет (unnamed package), которому соответствует текущий каталог (current working directory)

файловой системы. Вот поэтому у нас class-файл всегда оказывался в том же каталоге, что и соответствующий Java-файл.

Безымянный пакет служит обычно хранилищем небольших пробных или промежуточных классов. Большие проекты лучше хранить в пакетах. Например, библиотека классов Java 2 API хранится в пакетах java, javax, org.omg. Пакет Java содержит только подпакеты applet, awt, beans, io, lang, math, net, rmi, security, sql, text, util и ни одного класса. Эти пакеты имеют свои подпакеты, например, пакет создания ГИП и графики java.awt содержит подпакеты color, datatransfer, dnd, event, font, geometry, im,image, print.

Конечно, состав пакетов меняется от версии к версии.

Пакеты и интерфейсы

Пакет (package) — это некий контейнер, который используется для того, чтобы изолировать имена классов. Например, вы можете создать класс List, заключить его в пакет и не думать после этого о возможных конфликтах, которые могли бы возникнуть если бы кто-нибудь еще создал класс с именем List.

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

Пакеты

Все идентификаторы, которые мы до сих пор использовали в наших примерах, располагались в одном и том же пространстве имен (name space). Это означает, что нам во избежание конфликтных ситуаций приходилось заботиться о том, чтобы у каждого класса было свое уникальное имя. Пакеты — это механизм, который служит как для работы с пространством имен, так и для ограничения видимости. У каждого файла .java есть 4 одинаковых внутренних части, из которых мы до сих пор в наших примерах использовали только одну. Ниже приведена общая форма исходного файла Java.

  • одиночный оператор package (необязателен)
  • любое количество операторов import (необязательны)
  • одиночное объявление открытого (public) класса
  • любое количество закрытых (private) классов пакета (необязательны)

Оператор package

Первое, что может появиться в исходном файле Java — это оператор package, который сообщает транслятору, в каком пакете должны определяться содержащиеся в данном файле классы. Пакеты задают набор раздельных пространств имен, в которых хранятся имена классов. Если оператор package не указан, классы попадают в безымянное пространство имен, используемое по умолчанию. Если вы объявляете класс, как принадлежащий определенному пакету, например,

package java.awt.image;

то и исходный код этого класса должен храниться в каталоге java/awt/image.

Замечание

Каталог, который транслятор Java будет рассматривать, как корневой для иерархии пакетов, можно задавать с помощью переменной окружения СLASSPATH. С помощью этой переменной можно задать несколько корневых каталогов для иерархии пакетов (через ; как в обычном PATH).

Трансляция классов в пакетах

При попытке поместить класс в пакет, вы сразу натолкнетесь на жесткое требование точного совпадения иерархии каталогов с иерархией пакетов. Вы не можете переименовать пакет, не переименовав каталог, в котором хранятся его классы. Эта трудность видна сразу, но есть и менее очевидная проблема.

Представьте себе, что вы написали класс с именем PackTest в пакете test. Вы создаете каталог test, помещаете в этот каталог файл PackTest.Java и транслируете. Пока — все в порядке. Однако при попытке запустить его вы получаете от интерпретатора сообщение «can’t find class PackTest» («He могу найти класс PackTest»). Ваш новый класс теперь хранится в пакете с именем test, так что теперь надо указывать всю иерархию пакетов, разделяя их имена точками — test.PackTest. Кроме того Вам надо либо подняться на уровень выше в иерархии каталогов и снова набрать , либо внести в переменную CLASSPATH каталог, который является вершиной иерархии разрабатываемых вами классов.

Оператор import

После оператора package, но до любого определения классов в исходном Java-файле, может присутствовать список операторов import. Пакеты являются хорошим механизмом для отделения классов друг от друга, поэтому все встроенные в Java классы хранятся в пакетах. Общая форма оператора import такова:

import пакет1 [.пакет2].(имякласса|*);

Здесь пакет1 — имя пакета верхнего уровня, пакет2 — это необязательное имя пакета, вложенного в первый пакет и отделенное точкой. И, наконец, после указания пути в иерархии пакетов, указывается либо имя класса, либо метасимвол звездочка. Звездочка означает, что, если Java-транслятору потребуется какой-либо класс, для которого пакет не указан явно, он должен просмотреть все содержимое пакета со звездочкой вместо имени класса. В приведенном ниже фрагменте кода показаны обе формы использования оператора import :

import java.util.Date import java.io.*;

Замечание

Но использовать без нужды форму записи оператора import с использованием звездочки не рекомендуется, т.к. это может значительно увеличить время трансляции кода (на скорость работы и размер программы это не влияет).

Все встроенные в Java классы, которые входят в комплект поставки, хранятся в пакете с именем java. Базовые функции языка хранятся во вложенном пакете java.lang. Весь этот пакет автоматически импортируется транслятором во все программы. Это эквивалентно размещению в начале каждой программы оператора

import java.lang.*;

Если в двух пакетах, подключаемых с помощью формы оператора im-port со звездочкой, есть классы с одинаковыми именами, однако вы их не используете, транслятор не отреагирует. А вот при попытке использовать такой класс, вы сразу получите сообщение об ошибке, и вам придется переписать операторы import, чтобы явно указать, класс какого пакета вы имеете ввиду.

class MyDate extends Java.util.Date

Ограничение доступа

Java предоставляет несколько уровней защиты, обеспечивающих возможность тонкой настройки области видимости данных и методов. Из-за наличия пакетов Java должна уметь работать еще с четырьмя категориями видимости между элементами классов :

  • Подклассы в том же пакете.
  • Не подклассы в том же пакете.
  • Подклассы в различных пакетах.
  • Классы, которые не являются подклассами и не входят в тот же пакет.

В языке Java имеется три уровня доступа, определяемых ключевыми словами: private (закрытый), public (открытый) и protected (защищенный), которые употребляются в различных комбинациях. Содержимое ячеек таблицы определяет доступность переменной с данной комбинацией модификаторов (столбец) из указанного места (строка).

private модификатор отсутствует private protected protected public
тот же класс да да Да да да
подкласс в том же пакете нет да Да да да
независимый класс в том же пакете нет да Нет да да
подкласс в другом пакете нет нет Да да да
независимый класс в другом пакете нет нет Нет нет да

На первый взгляд все это может показаться чрезмерно сложным, но есть несколько правил, которые помогут вам разобраться. Элемент, объявленный public, доступен из любого места. Все, что объявлено private, доступно только внутри класса, и нигде больше. Если у элемента вообще не указан модификатор уровня доступа, то такой элемент будет виден из подклассов и классов того же пакета. Именно такой уровень доступа используется в языке Java по умолчанию. Если же вы хотите, чтобы элемент был доступен извне пакета, но только подклассам того класса, которому он принадлежит, вам нужно объявить такой элемент protected. И наконец, если вы хотите, чтобы элемент был доступен только подклассам, причем независимо от того, находятся ли они в данном пакете или нет — используйте комбинацию private protected.

Ниже приведен довольно длинный пример, в котором представлены все допустимые комбинации модификаторов уровня доступа. В исходном коде первого пакета определяется три класса: Protection, Derived и SamePackage. В первом из этих классов определено пять целых переменных — по одной на каждую из возможных комбинаций уровня доступа. Переменной n приписан уровень доступа по умолчанию, n_pri — уровень private, n_pro — protected, n_pripro — private protected и n_pub — public. Во всех остальных классах мы пытаемся использовать переменные первого класса. Те строки кода, которые из-за ограничения доступа привели бы к ошибкам при трансляции, закомментированы с помощью однострочных комментариев (//) — перед каждой указано, откуда доступ при такой комбинации модификаторов был бы возможен. Второй класс — Derived — является подклассом класса Protection и расположен в том же пакете р1. Поэтому ему доступны все перечисленные переменные за исключением n_pri. Третий класс, SamePackage, расположен в том же пакете, но при этом не является подклассом Protection. По этой причине для него недоступна не только переменная n_pri, но и n_pripro, уровень доступа которой — private protected.

package р1; public class Protection < int n = 1; private int n_pri = 2; protected int n_pro = 3; private protected int n_pripro = 4; public int n_pub = 5; public Protection() < System.out.println("base constructor"); System.out.println("n="+n); System.out.println("n_pri="+n_pri); System.out.println("n_pro="+n_pro); System.out.println("n_pripro="+n_pripro); System.out.println("n_pub="+n_pub); >> class Derived extends Protection < Derived() < System.out.println("derived constructor"); System.out.println("n = " + n); // только в классе // System.out.println("n_pri="+n_pri); System.out.println("n_pro="+n_pro); System.out.println("n_pripro="+n_pripro); System.out.println("n_pub="+n_pub); >> class SamePackage < SamePackage() < Protection p = new Protection(); System.out.println("same package constructor"); System.out.println("n="+p.n); // только в классе // System.out.println("n_pri="+p.n_pri); System.out.println("n_pro="+p.n_pro); // только в классе и подклассе // System.out.println("n_pripro="+p.n_pripro): System.out.println("n_pub callback called with " + p); >>

В очередном примере метод callback интерфейса, определенного ранее, вызывается через переменную — ссылку на интерфейс:

class TestIface < public static void main(String args[]) < Callback с = new client(); c.callback(42); >>

Ниже приведен результат работы программы:

С:\> Java TestIface callback called with 42

Переменные в интерфейсах

Интерфейсы можно использовать для импорта в различные классы совместно используемых констант. В том случае, когда вы реализуете в классе какойлибо интерфейс, все имена переменных этого интерфейса будут видимы в классе как константы. Это аналогично использованию файлов-заголовков для задания в С и C++ констант с помощью директив #define или ключевого слова const в Pascal / Delphi.

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

import java.util.Random; interface SharedConstants < int NO = 0; int YES = 1; int MAYBE = 2; int LATER = 3; int SOON = 4; int NEVER = 5; >class Question implements SharedConstants < Random rand = new Random(); int ask() < int prob = (int) (100 * rand.nextDouble()); if (prob 

Обратите внимание на то, что результаты при разных запусках программы отличаются, поскольку в ней используется класс генерации случайных чисел Random пакета java.util. Описание этого пакета приведено в главе 12.

С:\> Java AskMe Later Scon No Yes

Использование пакетов

Теперь вы обладаете полной информацией для создания собственных пакетов классов. Легко понимаемые интерфейсы позволят другим программистам использовать ваш код для самых различных целей. Инструменты, которые вы приобрели, изучив эту и предыдущую главы, должны вам помочь при разработке любых объектно-ориентированных приложений. В дальнейшем вы познакомитесь с некоторыми важными специфическими свойствами Java, которые представлены в виде классов в пакете java.lang. В трех последующих главах вы освоите работу с текстовыми строками, параллельное программирование и обработку исключительных ситуаций.

Что такое менеджер пакетов в программировании

У разработчиков есть суперполезный инструмент для управления программами на компьютере — менеджер пакетов. Через него можно ставить и удалять софт, скачивать вспомогательные библиотеки, подтягивать все зависимые библиотеки и обновлять систему.

Пакетные менеджеры выглядят сложновато со стороны, но когда раскуришь их, становится понятно, легко и интересно. Расскажем обо всём этом сегодня.

Что такое пакет и чем он отличается от обычной программы

Пакет — это специальный набор файлов и данных. В пакете содержится:

  • сама программа, которую нужно установить;
  • файлы с настройкой — куда что копировать;
  • информация о том, какие ещё пакеты или библиотеки нужны для установки;
  • команды и другие действия, которые нужно будет выполнить во время установки;
  • сведения о версии программы.

В обычной программе-установщике всё это тоже есть, но установщик может сам выполнить все необходимые команды и установить программу, а пакет — нет. Для установки и управления пакетами как раз и нужен менеджер пакетов.

Зависимости и менеджер пакетов

Кроме установки, у менеджера пакетов много работы, но основная — распутать все зависимости. Зависимость пакетов — это когда для работы одного пакета требуется установка нескольких других, причём определённых версий.

Допустим, менеджер пакетов ставит пакет «Волшебный веб-сайт за 10 минут», смотрит его зависимости и понимает, что для работы ему нужны:

  • база данных MySQL 7-й версии;
  • утилита ssh любой версии;
  • веб-сервер Apache версии 2.4.0 или позднее.

Менеджер пакетов смотрит в список уже установленных программ и видит, что ssh уже стоит, Apache есть версии 2.4.2, а MySQL вообще не установлена. Это значит, что он первую установку ставит на паузу и начинает качать и устанавливать пакет с MySQL 7-й версии. С ним повторяется то же самое — менеджер смотрит, какие пакеты указаны в зависимостях, качает их и ставит. Как только всё установлено — возвращается к первоначальной задаче и ставит пакет «Волшебный веб-сайт за 10 минут».

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

Удаление пакетов

Отдельная задача менеджера пакетов — проследить, чтобы при удалении пакета также удалились и все его зависимости (то, что нужно было ему для работы). Но при этом менеджер посмотрит, а не использует ли другой пакет что-то из этих зависимостей. Если использует — менеджер не будет её удалять, а оставит для другой программы.

Что такое репозиторий

Репозиторий — это место, где хранятся пакеты. Проще говоря, какие-то чужие компьютеры.

Обычно у каждого семейства операционных систем свой репозиторий и свой менеджер пакетов, который с ним работает. Например:

  • dpkg — менеджер пакетов для Debian. В репозитории Debian огромное количество пакетов — он работает с 1994 года.
  • rpm — менеджер пакетов для RedHat Linux.
  • portage — менеджер для Gentoo. Его особенность в том, что все программы компилируются при установке под то железо и процессор, который установлен в системе. Это даёт прирост производительности, но усложняет установку программ.

Почти все менеджеры пакетов работают из командной строки — это стандарт в Linux- и UNIX-системах. Например, команда rpm MySQL-server-standard-5.0.24-0.rhel4.i386.rpm установит MySQL в Red Hat Linux, а команда emerge --ask dev-db/phpmyadmin скачает исходные файлы панели управления базы данных и скомпилирует их в Gentoo Linux:

Что такое менеджер пакетов в программировании

Менеджеры пакетов в программировании

На самом деле мы уже пользовались менеджерами пакетов, когда делали свои проекты:

  • Запускаем нейросеть на домашнем компьютере
  • Моделируем игру в рулетку на Python
  • Телеграм-бот на Python

Здесь мы использовали pip — менеджер пакетов для Python. С его помощью мы устанавливали все необходимые библиотеки, без которых приложение бы не работало. Например, вот что можно сделать с pip:

pip install plotly — установить библиотеку plotly, чтобы мы могли рисовать графики

pip check pandas — проверить зависимости у выбранного пакета

pip uninstall telebot — удалить библиотеку для работы с телеграм-ботом

А для проекта с тепловыми картами твитов Байдена и Трампа нам нужны были три библиотеки, и мы использовали команду pip list, чтобы посмотреть, есть они у нас или нет.

Что такое менеджер пакетов в программировании

А в проекте с менеджером паролей мы вовсю использовали npm — менеджер пакетов Node.js: с его помощью мы установили Электрон и превратили веб-проект в отдельное приложение:

Что такое менеджер пакетов в программировании

Вот ещё несколько команд npm:

npm update -g npm — обновить сам менеджер пакетов

npm install mysql — установить базу данных MySQL

npm install-test — установить пакет и проверить тестами его работу

Бытовые менеджеры

В каком-то смысле магазины приложений типа AppStore и RuStore — это тоже менеджеры пакетов:

  • Программы хранятся централизованно на каких-то серверах и представлены в каталоге.
  • Установкой и обновлением приложений занимается сам магазин приложений.
  • Если приложению что-то нужно, магазин сам проверит наличие этого и установит при необходимости.

Вместе с тем на Android и Mac OS можно установить приложения в обход сторов. Кому что удобнее.

Когда менеджера пакетов нет

В Windows каждое приложение отвечает само за себя: если нужно что-то установить, обновить или удалить, вы просто это делаете. С одной стороны, это удобно, а с другой не очень:

  • Программы часто связаны между собой, и если вручную удалить одну, сломается другая.
  • Если поставить новую версию, то от старой могут остаться ненужные файлы и старые версии, которые будут занимать место.
  • При обновлении программа может обновить и свои библиотеки, которые используют другие программы. Но они рассчитывали на старые версии, а с новыми могут перестать работать.

Жить с этим можно, но это не лучший вариант.

Что дальше

Чтобы было интереснее, мы установим Linux в виртуальную машину и поэкспериментируем с менеджером пакетов — посмотрим, что он умеет, чем полезен и как с ним работать.

Читаете «Код»? Зарабатывайте на коде

Сфера ИТ и разработки постоянно растёт и требует новых кадров. Компании готовы щедро платить даже начинающим разработчикам, а опытных вообще отрывают с руками. Обучиться на разработчика можно в «Яндекс Практикуме».

Читаете «Код»? Зарабатывайте на коде Читаете «Код»? Зарабатывайте на коде Читаете «Код»? Зарабатывайте на коде Читаете «Код»? Зарабатывайте на коде

Получите ИТ-профессию

В «Яндекс Практикуме» можно стать разработчиком, тестировщиком, аналитиком и менеджером цифровых продуктов. Первая часть обучения всегда бесплатная, чтобы попробовать и найти то, что вам по душе. Дальше — программы трудоустройства.

Что такое пакет в программировании

Хотя корни Ады лежат в языке Паскаль, концепция пакетов была заимствована из других языков программирования и подверглась значительному влиянию последних наработок в области разработки программного обеспечения обсуждавшихся в 1970-х годах. Несомненно, что одной из главных инноваций в этот период была концепция программного модуля (или пакета).

Следует заметить, что концепция пакетов не рассматривает то, как программа будет выполняться. Идея данного подхода посвящена проблеме построения программного комплекса, облегчению понимания того как он устроен и, следовательно, упрощению его сопровождения.

7.1 Общие сведения о пакетах Ады

7.1.1 Идеология концепции пакетов

    Примечание:
    В системе компилятора GNAT существует соглашение согласно которому файлы спецификаций имеют расширение ads (ADa Specification), а файлы тел имеют расширение adb (ADa Body).

7.1.2 Спецификация пакета

Как уже говорилось, спецификация пакета Ады определяет интерфейс доступа к вычислительным ресурсам (сервисам) пакета. Она может содержать описания типов, переменных, констант, спецификации подпрограмм, других пакетов, - все то, что должно быть доступно тому, кто будет использовать данный пакет. Простой пример спецификации пакета может иметь следующий вид:

 package Odd_Demo is type A_String is array (Positive range <>) of Character; Pi : constant Float := 3.14; X : Integer; type A_Record is record Left : Boolean; Right : Boolean; end record; -- примечательно, что дальше, для двух подпрограмм представлены только -- их спецификации, тела этих подпрограмм будут находиться в теле пакета procedure Insert(Item : in Integer; Success : out Boolean); function Is_Present(Item : in Integer) return Boolean; end Odd_Demo; 

Мы можем получить доступ к этим сервисам в нашем коде путем указания данного пакета в спецификаторе совместности контекста with, а затем использовать полную точечную нотацию. Полная точечная нотация, в общем случае, имеет следующий вид:

 имя_пакета.имя_используемого_ресурса 

где имя_используемого_ресурса - это имя типа, подпрограммы, переменной и т.д.

Для демонстрации сказанного приведем схематический пример процедуры которая использует показанную выше спецификацию:

 with Odd_Demo; procedure Odder_Demo is My_Name : Odd_Demo.A_String; Radius : Float; Success : Boolean; begin Radius := 3.0 * Odd_Demo.Pi; Odd_Demo.Insert(4, Success); if Odd_Demo.Is_Present(34) then . . . . end Odder_Demo; 

Не трудно заметить, что доступ к ресурсам пакета, текстуально подобен доступу к полям записи.

В случаях, когда использование полной точечной нотации для доступа к ресурсам пакета обременительно, можно использовать инструкцию спецификатора использования контекста use. Это позволяет обращаться к ресурсам которые предоставляет данный пакет без использования полной точечной нотации, так, как будто они описаны непосредственно в этом же коде.

 with Odd_Demo; use Odd_Demo; procedure Odder_Demo is My_Name : A_String; Radius : Float; Success : Boolean; begin Radius := 3.0 * Pi; Insert(4, Success); if Is_Present(34) then . . . . end Odder_Demo; 

Если два пакета, указаны в инструкциях with и use в одном компилируемом модуле (например, в подпрограмме или другом пакете), то возможно возникновение коллизии имен используемых ресурсов, которые предоставляются двумя разными пакетами. В этом случае можно избавиться от двусмысленности путем возвращения к использованию полной точечной нотации для соответствующих ресурсов.

 ------------------------------- package No1 is A, B, C : Integer; end No1; ------------------------------- package No2 is C, D, E : Integer; end No2; ------------------------------- with No1; use No1; with No2; use No2; procedure Clash_Demo is begin A := 1; B := 2; C := 3; -- двусмысленность, мы ссылаемся -- на No1.c или на No2.c? No1.C := 3; -- избавление от двусмысленности путем возврата No2.C := 3; -- к полной точечной нотации end Clash_Demo; 

Может возникнуть другая проблема - когда локально определенный ресурс "затеняет" ресурс пакета указанного в инструкции use. В этом случае также можно избавиться от двусмысленности путем использования полной точечной нотации.

 package No1 is A : Integer; end No1; with No1; use No1; procedure P is A : Integer; begin A := 4; -- это - двусмысленно P.A := 4; -- удаление двусмысленности путем указания -- имени процедуры в точечной нотации No1.A := 5; -- точечная нотация для пакета end P; 
7.1.3 Тело пакета

Тело пакета содержит все детали реализации сервисов, указаных в спецификации пакета. Схематическим примером тела пакета, для показанной выше спецификации, может служить:

 package body Odd_Demo is type List is array (1..10) of Integer; Storage_List : List; Upto : Integer; procedure Insert(Item : in Integer; Success : out Boolean) is begin . . . end Insert; function Is_Present(Item : in Integer) return Boolean is begin . . . end Is_Present; begin -- действия по инициализации пакета -- это выполняется до запуска основной программы! for I in Storage_List'Range loop Storage_List(I) := 0; end loop; Upto := 0; end Odd_Demo; 

Все ресурсы, указанные в спецификации пакета, будут непосредственно доступны в теле пакета без использования дополнительных инструкций спецификации контекста with и/или use.

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

Переменные, описанные в теле пакета, сохраняют свои значения между успешными вызовами публично доступных подпрограмм пакета. Таким образом, мы можем создавать пакеты, которые сохраняют информацию для более позднего использования (другими словами: сохранять информацию о состоянии).

Раздел "begin . end", в конце тела пакета, содержит перечень инструкций инициализации для этого пакета. Инициализация пакета выполняется до запуска на выполнение главной подпрограммы. Это справедливо для всех пакетов. Следует заметить, что стандарт не определяет порядок выполнения инициализации различных пакетов.

7.2 Средства сокрытия деталей реализации внутреннего представления данных

Как уже указывалось, спецификация пакета определяет интерфейс, а его тело скрывает реализацию сервисов предоставляемых пакетом. Однако, в примерах, показанных ранее, от пользователей пакетов были скрыты только детали реализации подпрограмм, а все типы данных были открыто описаны в спецификации пакета. Следовательно, все детали реализации представления внутренних структур данных "видимы" пользователям. В результате, пользователи таких пакетов, полагаясь на открытость представления внутренних структур данных и используя эти сведения, попадают в зависимость от деталей реализации структур данных. Это значит, что в случае какого-либо изменения во внутреннем представлении данных, как минимум, возникает необходимость в полной перекомпиляции всех программных модулей которые используют такую информацию. В худшем случае, зависимые модули придется не только перекомпилировать, но и переделать.

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

Ада позволяет "закрыть" детали внутреннего представления данных и, таким образом, избавиться от проблемы массовых переделок. Такие возможности обеспечивает использование приватных типов (private types) и лимитированных приватных типов (limited private types).

7.2.1 Приватные типы (private types)

    - изъятие средств (Withdraw)
    - размещение средств (Deposit)
    - создание счета (Create)
 package Accounts is type Account is private; -- описание будет представлено позже procedure Withdraw(An_Account : in out Account; Amount : in Money); procedure Deposit( An_Account : in out Account; Amount : in Money); function Create( Initial_Balance : Money) return Account; function Balance( An_Account : in Account) return Integer; private -- эта часть спецификации пакета -- содержит полные описания type Account is record Account_No : Positive; Balance : Integer; end record; end Accounts; 
    - присваивание
    - проверка на равенство (не равенство)
    - проверки принадлежности ("in", "not in")
 with Accounts; use Accounts; procedure Demo_Accounts is Home_Account : Account; Mortgage : Account; This_Account : Account; begin Mortgage := Accounts.Create(Initial_Balance => 500.00); Withdraw(Home_Account, 50); . . . This_Account := Mortgage; -- присваивание приватного типа - разрешено -- сравнение приватных типов if This_Account = Home_Account then . . . end Demo_Accounts; 

7.2.2 Лимитированные приватные типы (limited private types)

Хотя приватные типы позволяют разработчику получить значительный контроль над действиями пользователя, ограничив способности пользователя в манипулировании объектами, бывают случаи когда необходимо запретить пользователю приватного типа использовать даже такие предопределенные операции как сравнение и присваивание.

В качестве демонстрации сказанного, рассмотрим следующий пример:

 package Compare_Demo is type Our_Text is private; . . . private type Our_Text (Maximum_Length : Positive := 20) is record Length : Index := 0; Value : String(1..Maximum_Length); end record; . . . end Compare_Demo; 

Здесь, тип Our_Text описан как приватный и представляет из себя запись. В данной записи, поле длины Length определяет число символов которое содержит поле Value (другими словами - число символов которые имеют смысл). Любые символы, находящиеся в позиции от Length + 1 до Maximum_Length будут нами игнорироваться при использовании этой записи. Однако, если мы попросим компьютер сравнить две записи этого типа, то он, в отличие от нас, не знает предназначения поля Length. В результате, он будет последовательно сравнивать значения поля Length и значения всех остальных полей записи. Очевидно, что алгоритм предопределенной операции сравнения в данной ситуации не приемлем, и нам необходимо написать собственную функцию сравнения.

Для подобных случаев Ада предусматривает лимитированные приватные типы. Изменим рассмотренный выше пример следующим образом:

    . . . procedure Init (T : in out Our_Text; S : in String); . . .

Напомним, что спецификация такой процедуры должна быть размещена в спецификации пакета Compare_Demo, а ее тело (реализация) - в теле этого пакета.

7.2.3 Отложенные константы (deferred constants)

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

 package Coords is type Coord is private; Home: constant Coord; -- отложенная константа! private type Coord is record X : Integer; Y : Integer; end record; Home : constant Coord := (0, 0); end Coords; 

В результате такого описания, пользователи пакета Coords "видят" константу Home, которая имеет приватный тип Coord, и могут использовать эту константу в своем коде. При этом, детали внутреннего представления этой константы им не доступны и они могут о них не заботиться.

7.3 Дочерние модули (child units) (Ada95)

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

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

Дочерние модули непосредственно используются в стандартной библиотеке Ады. В частности, дочерними модулями являются такие пакеты как Ada.Text_IO, Ada.Integer_Text_IO. Следует также заметить, что концепция дочерних модулей была введена стандартом Ada95. В стандарте Ada83 эта идея отсутствует.

7.3.1 Расширение существующего пакета

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

Следовательно, для логического расширения уже существующего пакета предпочтительнее использовать дочерний пакет который будет существовать абсолютно отдельно. Например:

 package Stacks is type Stack is private; procedure Push(Onto : in out Stack; Item : Integer); procedure Pop(From : in out Stack; Item : out Integer); function Full(Item : Stack) return Boolean; function Empty(Item : Stack) return Boolean; private -- скрытая реализация стека . -- точка  A  end Stacks; package Stacks.More_Stuff is function Peek(Item : Stack) return Integer; end Stacks.More_Stuff; 

    Примечание:
    Согласно правил именования файлов, принятым в системе компилятора GNAT, спецификация и тело пакета Stacks должны быть помещены в файлы:

      stacks.adsи stacks.adb
      stacks-more_stuff.adsи stacks-more_stuff.adb
     with Stacks.More_Stuff; procedure Demo is X : Stacks.Stack; begin Stacks.Push(X, 5); if Stacks.More_Stuff.Peek = 5 then . . . end Demo; 

    Следует заметить, что включение дочернего пакета в инструкцию спецификатора совместности контекста with автоматически подразумевает включение в инструкцию with всех пакетов-родителей. Однако, инструкция спецификатора использования контекста use таким образом не работает. То есть, область видимости может быть получена пакетом только в базисе пакета.

     with Stacks.More_Stuff; use Stacks; use More_Stuff; procedure Demo is X : Stack; begin Push(X, 5); if Peek(x) = 5 then . . . end Demo; 

    Необходимо также заметить, что подпрограммы (процедуры и функции) могут быть дочерними модулями пакета (правила их использования достаточно очевидны). При этом, однако, сами подпрограммы не могут иметь дочерние модули.

    7.3.2 Иерархия модулей как подсистема

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

    В подобных ситуациях, используя концепцию дочерних модулей Ады, можно выделить каждую отдельно разрабатываемую подсистему в самостоятельную иерархию модулей. При этом, каждая подсистема будет иметь свой собственный корневой пакет с уникальным именем.

     package Root is -- корневой пакет может быть пустым end Root; 

    Поскольку имя корневого пакета уникально, то имена дочерних модулей иерархии также будут уникальны, что, в результате, минимизирует вероятность коллизии имен. Кроме того, такой прием является удобным средством разделения большого проекта на логически самостоятельные составные части.

    Примером использования подобного подхода может служить стандартная библиотека Ады, которая предстявляется как набор дочерних модулей трех корневых пакетов: Ada, Interfaces и System.

    7.3.3 Приватные дочерние модули (private child units)

    Дочерние модули могут быть приватными. Такая концепция позволяет создавать дочерние модули, которые будут видимы только внутри иерархии родительского пакета. В этом случае сервисы для подсистемы могут быть инкапсулированы внутри приватных пакетов, используя наследуемые ими преимущества для компиляции и видимости.

    В спецификацию приватных дочерних пакетов разрешается включать приватную часть спецификации их пакетов-родителей, поскольку такие дочерние модули - приватны. В обычном случае - это не разрешается, так как нарушает сокрытие деталей реализации родительской приватной части.

     private package Stacks.Statistics is procedure Increment_Push_Count; end Stacks.Statistics; 

    Процедура Stacks.Statistics.Increment_Push_Count могла бы быть вызвана внутри реализации пакета Stacks. Такая процедура не будет доступна ни одному внешнему, по отношению к этой иерархии модулей, клиенту.

    Copyright (C) А.Гавва V-0.4w май 2004

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

Ваш адрес email не будет опубликован. Обязательные поля помечены *