Файл: Классификация языков программирования. Критерии выбора среды и языка разработки программ (Основная терминология программирования).pdf
Добавлен: 28.03.2023
Просмотров: 350
Скачиваний: 2
СОДЕРЖАНИЕ
1. Теоретические концепции языков программирования и программных сред
1.1. Основная терминология программирования
1.2. Классификация языков программирования по способу исполнения программ
1.3. Классификация языков программирования по уровню детализации
1.4. Классификация парадигм, реализуемых языками программирования
1.5. Критерии выбора сред и языков программирования
2. Некоторые современные языки программирования и их сравнение
2.2. Язык программирования C++
2.3. Язык программирования Java
2.4. Язык программирования Python
2.6. Сравнительный анализ языков программирования с точки зрения одной из отраслей
3. Некоторые современные среды разработки и их сравнение
3.1. Среда разработки Microsoft Visual Studio
3.2. Среда разработки NetBeans
3.4. Сравнительный анализ сред разработки приложений с точки зрения одной из отраслей
2.2. Язык программирования C++
На сегодняшний день, культура написания кода на языке C++ и его хорошее понимание очень высоко ценится программистами-практиками[93]. Он был разработан в 70-х Б. Страуструпом, с целью реализовать некоторые возможности объектно-ориентированного языка Simula на языке с синтаксисом C. В Simula создателя привлекала программная сама организация программы. Изначально рабочим названием было «C с классами». Из изначальных особенностей можно выделить классы и возможность создания их иерархии, контроль доступа с помощью ключевых слов private и public, конструкторы и деструкторы, дружественные классы.
Окончательную форму и название язык приобрёл к 1983 году[94]. В существенно доработанной версии ЯП, которая и получила название C++, также присутствовали глобальный[95] и ad-hoc полиморфизм, реализованный за счёт виртуальных методов, перегрузки методов и операторов, возможность создать ссылку на объект, улучшенная по сравнению с классическим Си концепция неизменяемых данных[96].
По критерию исполнения программы, C++ является компилируемым. Компилятор этого языка имеет очень мощную систему связывания файлов исходного текста перед компиляцией. Она позволяет, в частности, компилировать программу одновременно из нескольких исходников, не только на C++, но и на Си, Фортране или Ассемблере[97]. Чтобы незначительно влиять на этап компиляции из исходного текста, существуют (как и в классическом Си) директивы препроцессора. Каждая директива описывается в файлах исходных кодов и начинается со знака #. Препроцессор обрабатывает каждую строку, начинающуюся с этого символа, перед компиляцией[98].
Язык C++, в отличие от C, является уже полностью высокоуровневым. Реализация языка не зависит от платформы напрямую, а также допускает высокие уровни абстракции. Программист может работать с осмысленными и читабельными данными, не относящимися к работе компьютера. Даже работа с памятью может быть реализована на высоком уровне, с помощью потокового ввода-вывода[99]. К тому же, одна программа на C++ легко может быть скомпилирована в нескольких вариантах для разных платформ [21]. Однако в C++ сохранены и многие низкоуровневые возможности. В частности, можно ограниченно применять регистровую память, также можно использовать и низкоуровневую работу с оперативной памятью компьютера. Непосредственная работа с ОЗУ осуществляется с помощью низкоуровневых указателей[100].
Этот ЯП позволяет программисту писать код так, как ему удобней. В частности, для высокоуровневого программирования поддерживаются классы (при этом, современный C++ является одним из ООП-языков, поддерживающих множественное наследование[101]), а для низкоуровневого – возможность писать императивный код в стиле классического Си, поддерживая большинство приёмов программирования на нём[102]. Б. Страуструп отмечал, что намеренно не стал принуждать использовать исключительно одну парадигму, а дал возможность программировать так, как потенциальный программист посчитает нужным[103]. Таким образом, C++ можно назвать универсальным, мультипарадигмальным языком. Обратной стороной столь широких возможностей является постоянно усложняющийся синтаксис с огромным количеством лексем[104], затрудняющий обучение языку, и возможность испортить код неосторожными низкоуровневыми действиями.
Применение объектно-ориентированной модели на C++ гарантирует инкапсуляцию некоторой информации. В частности, данные каждого класса делятся на спецификаторы доступа public, protected и private. Данные, отмеченные как private и public, недоступны извне класса (но последнее доступно при наследовании). Таким образом, можно контролировать уровни доступа к членам класса и обезопасить код. Есть и довольно необычный способ обеспечить доступ к члену класса: это пометить функцию или класс как дружественный для другого класса (см. приложение 3). Это разрешает избирательный доступ к отмеченной информации дружественным типам[105]. Какой-либо объект данных можно объявить и вне контекста класса. Тогда он станет виден в пределах всей программы.
Полиморфизм методов реализован за счёт их переопределения. Метод, доступный для переопределения, называется виртуальным (virtual). В современных версиях C++ виртуальные функции могут и вовсе не иметь своего тела (см. приложение 3). Классы с такими методами, лишь представляют некий интерфейс для реализации потомками, и называются абстрактными[106].
Создатель языка C++ решил не делать реализации схемы со сборкой мусора. Эта идея обсуждалась, но так и не нашла реализации. Вместо неё было решено оставить классическую схему из Си, с явным выделением и освобождением памяти. В C++ пытались реализовать сборку мусора, но в основном эти идеи не увенчались успехом; хотя и существуют версии C++ со сборщиком мусора, он опционален [23].
Проблема инициализации и освобождения ресурсов была решена ещё в самом начале разработки языка, посредством системы с конструктором и деструктором. Конструктор в C++ представляет собой специальную функцию, присваивающую изначальные значения каждому члену экземпляра класса[107]. Деструктор же наоборот, освобождает ресурсы класса, когда он больше не нужен (см. приложение 3). По сути своей, это аналоги alloc и free из Си[108], но в рамках объектно-ориентированного программирования они более очевидны и лаконичны.
Основная библиотека с готовыми модулями на данном языке включает в себя (на примере Visual C++) обработчики ошибок, итераторы, средства ввода-вывода, средства управления памятью, зачаточные средства функционального программирования, средства синхронизации, высокоуровневую работу со строками[109]. Помимо этого, существует т.н. Стандартная Библиотека Шаблонов (STL). Её первоначальный вариант был разработан А. Степановым[110]. Она включает в себя достаточно много контейнеров данных и операции над ними. Это позволило реализовать на C++ некоторые возможности функционального программирования[111].
Суммируя (см. таблицу 2), универсальность C++ выше чем у Си. Надёжность кода незначительно повышается средствами обработки исключений и самим фактом наличия высокоуровневого программирования. Синтаксически переусложнён, поэтому целостность низкая. Стоимость трансляции и сопровождения высокая, выполнения – низкая.
2.3. Язык программирования Java
Этот язык был разработан инженерами компании Sun Microsystems (позднее – Oracle) в середине 1990-х годов. Задача, которую он решает, весьма сложна: необходимо было разработать ЯП, программы на котором можно бы было распространять по Интернету (в том числе в составе веб-страниц), запускать их на любом аппаратном обеспечении. Используемый в то время C++ не позволял решать такие вопросы безопасно[112]. В результате родилась концепция виртуальной машины Java (JVM), а также были разработаны и внедрены промышленные методы JIT-компиляции, и новый высокоуровневый язык. Создал концепцию этого ЯП Д. Гослинг. Java, согласно его работам [18], задумывался как простой и понятный язык, на котором можно быстро писать код без изнурительной подготовки. В числе потенциальных целей для языка значились клиент-серверные приложения, парадигмы передачи сообщений, многопоточные приложения с интерактивной графикой. ЯП был сделан понятным для программистов на таких языках как C++, Objective C, Eiffel и Ada [18, с. 19].
Как и Си, Java дважды становился языком года по версии TIOBE, и занимает стабильно высокие позиции в рейтингах ЯП [37, 48]. Это даёт повод считать, что влияние технологии JVM на IT-индустрию огромно.
Этот язык создавался изначально под нужды промышленного объектно-ориентированного программирования [18]. Присутствуют классы, из которых можно выстраивать иерархии. В классах наличествуют типизированные данные – поля, и типизированные функции – методы. Всё вышеперечисленное может быть инкапсулировано внутри класса (ключевым словом private), а может быть оставлено для публичного доступа (public). Существует и модель доступа только для потомков класса (protected). В каждом классе есть методы-конструкторы, инициализирующие его. Любая иерархия начинается от корневого класса Object[113]. Даже примитивные типы данных - символы и строки, массивы, числа и булевые значения, наследуют от него.
Присутствуют и абстрактные типы данных. Они делятся на 2 подвида. Первый подвид - абстрактные классы, отличающиеся от обычных только тем, что могут иметь методы без реализации и могут использоваться только для инициализации потомков. Второй абстрактный тип данных называется интерфейсом и целиком состоит из констант и сигнатур методов. Интерфейсы не наследуются классами, а реализуются (наследовать они могут только друг от друга), причём реализуются в обязательном порядке все методы, объявленные в интерфейсе. Можно реализовать или унаследовать сразу несколько интерфейсов (см. приложение 1). Никаких других реализаций множественного наследования в Java нет[114].
Полиморфизм реализован как переопределение методов. Методы абстрактных классов без реализации и интерфейсов тоже должны быть переопределены. Также возможна их перегрузка (см. приложение 1).
Java изначально проектировался специалистами Sun Microsystems как чистый объектно-ориентированный язык, и не планировалось какой-либо мультипарадигмальности. Однако, начиная с восьмой версии языка, реализованы некоторые средства функционального программирования. В частности, можно создавать функциональные интерфейсы. Такой интерфейс состоит из всего одной функции. Можно наследовать, реализовывать и использовать его, как любой интерфейс. Отличие от обычного интерфейса заключается в возможности инициализировать его метод по ходу выполнения программы ссылкой на метод или анонимную функцию, таким образом, реализовав интерфейс (см. приложение 1)[115].
Программы, написанные на Java, используют JIT-компиляцию либо классическую интерпретацию. На стороне программиста код преобразуется в промежуточную форму. Файлы с промежуточным кодом имеют расширение class и обычно собираются в распознаваемые JVM архивы с расширением .jar[116]. Промежуточный язык JVM может содержит 226 типов команд[117].
В изначальном описании 1995 года [18, с. 52 – 55], Java описывается как интерпретируемый язык. При классической интерпретации, JVM на стороне конечного пользователя преобразует байт-код в машинный код по мере выполнения. Этот способ довольно медленный, поэтому для него есть альтернатива: технология HotSpot. Компилятор HotSpot транслирует программу кусками и сохраняет в памяти. Иногда две технологии применяются совместно в целях оптимизации[118].
Большинство программных интерпретаторов виртуальной машины Java написаны на Си, что гарантирует им высокую портируемость на самые разные платформы. Существуют и аппаратные реализации JVM. Так, в частности, для аппаратной интерпретации байт-кода Java, существуют специальные микросхемы для его исполнения[119], или, например, технология Jazelle процессоров ARM [32]. Данный ЯП очень популярен в плане своей портируемости: на нём можно и писать приложения для браузеров, и создавать мобильные приложения, и писать прикладное ПО для ПК [37]. При этом программа на Java гарантирует свою работу везде, где есть JVM, без перекомпиляции. Платой за все эти плюсы стала низкая производительность программ, написанных на Java и невозможность низкоуровневого программирования. Первое связано со спецификой JIT-компиляции, а также частично вытекает из второго: синтаксис и семантика Java не предполагают работы ни с регистрами, ни с адресами в оперативной памяти, что вынуждает программиста отказаться от быстрых, но небезопасных, решений[120].
Для того чтобы не нагромождать программиста ненужной в практической деятельности работой, а также обеспечить стабильную работу JVM, процесс освобождения ресурсов в Java автоматизирован. За это отвечает сборщик мусора среды исполнения Java. Его работа невидима для программиста. Управление памятью Java базируется на подсчёте ссылок. Если ссылки на какой-либо объект программы есть, значит, он активен. Если же ссылки на объект в памяти отсутствуют, то рано или поздно он будет автоматически освобождён [18, с. 24 – 25]. Если надо подготовить объект к удалению, существуют методы-финализаторы. Их содержание может быть явно задано программистом. Финализаторы вызываются автоматически, перед освобождением неиспользуемого объекта во время процедуры сборки мусора. При отсутствии финализатора, сборщик мусора просто удаляет ненужный объект[121].