Українська   English  
ДонНТУ   Портал магистров

Реферат по теме выпускной работы

Содержание

Введение

Начнём с того, что передача опыта нужна во всех сферах деятельности, в том числе и в программировании. Несмотря на то, что западноевропейские исследователи говорят: серебряной пули нет [1], разумно предположить, что программирование можно и нужно автоматизировать.

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

За рабочим столом сидит программист, и пишет код. Вне зависимости от того, использует ли он готовые библиотеки, или готовые решения, он всё равно пишет код. Возможно, ему это занятие приносит эстетические удовлетворение. Но оно занимает много времени. Актуальным является вопрос сокращения временных затрат на создание программы. Так, чтобы программные проекты перестали срывать срок сдачи.

Несмотря на кажущуюся эффективность внедрения новых технологий программирования, остаётся необходимым следование принципам, десятилетиями апробированными в программной инженерии. Низкое качество программного продукта обусловлено несоблюдением этих принципов [2]. Например, попытка перечислить в одном модуле все типы геометрических фигур ведёт к необходимости изменения данного модуля каждый раз при появлении задачи со специфическими, ранее неизвестными, фигурами [3]. Действительно, всё знать невозможно, и программист, создающий подобную библиотеку должен предусмотреть возможность реализации новой функциональности путем добавления, а не изменения существующего модуля.

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

Целью данной работы является разработка нового способа автоматизации процесса программирования на основе использования принципов SOLID и применяя технологию синтеза программного кода с помощью онтологий.

В данной работе планируется решать следующие задачи;

  1. поиск существующих способов автоматизации программирования;
  2. разработка способа синтеза диаграммы классов;
  3. оценка полученных объектных модулей.

Научная новизна заключается именно в использовании принципов SOLID для автоматического синтеза диаграммы классов.

1. Обоснование практической применимости результатов исследования

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

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

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

Что отличает эти конструкции из классов от обычных схем, случайно собранных из базовых элементов, так это применение принципов SOLID. Паттерн, в котором эти принципы не соблюдаются, называют антипаттерном, напр. Active Record [4].

Желательно избегать использования антипаттернов. Тогда обеспечивается оптимальная расширяемость системы и предотвращается эффект снежного кома, когда планируется внести правки в один модуль, а получается переписывание программы [2].

Генерация UML-диаграмм [5] в сочетании с использованием CASE-средств, которые позволяют переводить диаграммы классов в код [6], существенно облегчает процесс создание объектно-ориентированной программы.

2. Обзор существующих решений

Итак, в чём отличие командного интерфейса от интерфейса окон [7]. Командный интерфейс оперирует языком команд. Этот тип интерфейса имеет текстовый редактор vim. В нем операции перемещения по строкам, вставка, удаление строк, операции с файлами производятся с помощью команд.

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

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

Если язык программирования является графическим, тогда пользовательский интерфейс компилятора будет графическим. Это справедливо в отношении транслятора графической нотации UML в объектно-ориентированный код языка текстового описания классов [6]. Представьте теперь, что компилятор текста программы имеет интерфейс форм. При каждой компиляции появляется окно с заданием параметров оптимизации, выбором набора инструкций и т.п. Конечно, в данном случае удобен интерфейс команд, Поскольку все эти параметры можно указать в аргументах вызова программы-компилятора и записать полученную строку-команду в файл автоматизации сборки.

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

Есть еще один момент по поводу многоуровневой базы знаний. Что такое вложенность, это детализация. Детализация это процесс внесения деталей в программный проект. А детали программного проекта имеют свои особенности на каждом уровне детализации.

Вложенность модулей знаний в отношении САПР ПО проявляется при рассмотрении уровней детализации программного проекта: уровень архитектуры и уровень программного кода. На уровне архитектуры описываются API. Это объявления функций в терминах языка Си. На уровне кода описываются высокоуровневые и низкоуровневые алгоритмы. Это определения функций в терминах языка Си.

Уровень программного кода имеет в свою очередь подуровни: высокоуровневые и низкоуровневые алгоритмы; описание алгоритма на псевдокоде и на конкретном языке программирования. Существенное отличие высокоуровневого языка от низкоуровневого языка алгоритмов в том, что они описывают порядок решения определенной задачи с помощью действий, понятных человеку. А подпрограммы нижнего уровня транслируют эти действия в язык, понятный ОС.

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

2.1 Сущность объектно-ориентированного программирования

Истолкование понятия объектно-ориентированного программирования лежит через языки, которые его поддерживают. Действительно, ведь программирование возможно только посредством языка. Следовательно, существование ООП неразрывно связано с языками программирования, которые его поддерживают. Разработчик языка C++ определяет ООП, как развитие парадигмы модульного программирование, которая добавляет возможность выражения различий между модулями путём наследования [3]. Принцип сокрытия данных, или т.н. инкапсуляция, изначально присутствовал в парадигме модульного программирования, в таких языках, как Модула-2. А полиморфизм изначально вообще был абстракцией данных, которая впоследствии стала частью ООП.

Несправедливо говорить, что переход к использованию ООП произошёл преждевременно [9]. Ведь изучение ООП сопряжено с его использованием, поскольку оно является методом программирования.

Хотя, можно использовать ООП в языках, которые не имеют изначально ООП парадигмы [10]. В таком случае полиморфный метод класса заменяется указателем на подпрограмму. Тогда в одном случае этот указатель указывает на процедуру создания слоя из отдельных нейронов, а в другом — на операцию записи матрицы в память видеопроцессора. В любом случае, сигнатуры этих функций должны быть одинаковыми. Если одна реализация для CPU, а другая для GPU, то программа сможет выбрать нужную реализацию в зависимости от наличие GPU в системе используя соответствующие алгоритмы.

2.1.1 Самосинтезируемость программ

Самый спорный и немного философский вопрос: способна ли машина самопрограммироваться [11]. Решение прикладных задач на ПК выполняется с помощью программного обеспечения, которое разрабатывают люди. Иными словами, человек обеспечивает машину программами, которые он сам и создаёт с помощью машины. Однако, зачем человеку утруждать себя выполнением данной трудозатратной операции, если можно часть работы переложить на машину.

Итак, есть два вида работ по созданию ПО: автоматизируемая [12] и неавтоматизируемая. В первую группу входят:

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

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

Таблица 1 — Соотношение автоматического и ручного труда
Работа человека Работа машины
кодирование алгоритма компиляция
документирование генерация документации
инспектирование статический анализ кода
реинженеринг реверс-инженеринг
составление диаграмм генерация кода по диаграмме
автоматизация сборки выполнение сборки программы
установка программы доставка программы
написание тестов выполнение тестов

Так и получается, что человек кодирует а машина компилирует. Почему не наоборот? Допустим, поменялись столбцы местами. Теперь машина придумывает алгоритмы, а человек должен их привести в действие. Пока машина диктует инструкции на своём языке, человеку приходится выполнять эти действия. Рано или поздно человеку надоедает выполнять однотипные операции, и у него появляется желание сократить, изменить порядок действий. А машина продолжает диктовать инструкции. Напрашивается вопрос: а зачем они нужны ему, ведь человек и без машины способен решать, какие действия ему выполнять. А машина, наоборот, нуждается в таком руководстве. Это свойство называется способность волеизъявления, которой у машины нет. Машина выполняет только те действия, которые нужны человеку, и только тогда, когда её заставят их выполнять.

Почему машина не может себя проинструктировать? Действия человека диктуются здравым смыслом и потребностями. У машины нет никаких потребностей, поскольку её создал человек для решения своих проблем. Неизвестно, что было бы если машина имела нужду в потреблении пищи... Хотя, действительно, машина потребляет электричество, и даже способна изъявлять свою потребность в нем при его недостатке. Однако это поведение запрограммировано человеком и осознание необходимости выжить, и что для этого нужно электричество, у машины отсутствует. Тогда, возможно, машина не инструктирует себя только потому, что для выполнения каких-либо действий ей нужно электричество. А так как это электричество нужно ей для функционирование, то она не желает тратить его понапрасну. Правильно, а зачем что-либо делать если эта затратная операция не приносит пользы. В чём заключается польза действий для машины? Да в оптимизации работы системы, например. А смысл оптимизации в том, чтобы сократить трату ресурсов ПК. А сокращают трату для снижения материальных затрат и ускорения решения прикладных задач. А это нужно, в свою очередь, только человеку, поскольку ПК всё равно, сколько он съест электричества и насколько хватит ему видеокарты, человек всё равно всё ему предоставляет. В итоге получается, что все действия машины нужны только человеку, а машине они не нужны. Вот поэтому машина не самопрограммируется: у неё нет потребности в каких либо действиях. А человеку надо, ему всё мало, нужно больше. Он создал ракету, двигатель, компьютер, окутал мир оптоволоконным кабелем, и сидит, радуется. А компьютер безропотно выполняет его поручения.

2.2 Методы автоматизации кодирования алгоритмов

Если поменять местами столбцы нельзя, то остаётся пытаться сместить центр тяжести в пользу автоматизации кодирования алгоритмов. Да, алгоритмы нужны человеку, а не машине. Поэтому только человек принимает решение о создании алгоритма. А для того, чтобы передать его машине нужен общий язык, который будет понятен и человеку, и компьютеру. Итак задача человека — записать алгоритм на таком языке, а компьютер будет этот алгоритм выполнять. Для того и нужны языки программирования, и от них нельзя избавиться по той причине, что они обеспечивают коммуникативную связь человека с компьютером. Приходим к пониманию того, что смещение центра тяжести в пользу автоматизации производится путём облегчения процесса кодирования алгоритмов [18]. При этом язык меняется, но должен оставаться полным и сохранять однозначность. Полнота языка означает возможность его средствами объяснять ход решения любой существующей задачи. Чего принципиально нового можно придумать в данной области, что облегчило бы процесс кодирования.

В отдельных случаях, графический язык программирования, напр. Scratch [19] (этот же язык положен в основу авторского проекта по визуальному программированию на Java [20]), способен облегчить кодирование алгоритмов при обучении программированию. Кодирование алгоритмов производится путем перетаскивания заготовок готовых блоков кода в окне редактирования. Есть ещё несколько визуальных языков программирования: ДРАКОН.

Язык ДРАКОНа основан на парадигме двумерного структурного программирования [21]. Это значит, что алгоритмы описываются в виде чертежа, а не текста. Двухмерность состоит в том, что структурные блоки алгоритма расположены на плоскости чертежа так, что зрение проектировщика перемещается как горизонтально так и вертикально при чтении алгоритма. Примерно тоже самое происходит в случае написания многопоточных программ: проектировщику приходится часто перемещаться между несколькими параллельными потоками работ. В ДРАКОНе для описания многопоточности используются блоки распараллеливания и синхронизации, как на диаграммах деятельности UML. Составными частями ДРАКОН-схемы (т.н. иконы) являются элементы блок-схем и некоторые дополнительные блоки.

До сих пор нет крупного проекта, который полностью написан на визуальном языке программирования [22]. Обязательно встретится честь кода на другом языке. Основные причины низкой популярности визуальных языков программирования:

Хотя, последние две причины не имеют отношения к ДРАКОНу. Действительно, этот язык имеет связь с другими текстовыми языками. Текстовый синтаксис позаимствованного языка позволяет использовать библиотеки, написанные на нём. Существуют разные вариации ДРАКОНов, каждая из которых отличается синтаксисом текстовой части. И что касается третьего замечания, то оно не действительно в отношении ДРАКОНа, поскольку не все реализации этого языка имеют коммерческую лицензию. Тогда что же обуславливает низкую популярность этого языка? Возможно, его узкая направленность. Несмотря на то, что блок-схемы способны описать любой алгоритм, в то время, как текстовые языки предусматривают конечное число ключевых слов, ДРАКОН оперирует конечным числом графических нотаций вместо этих слов. Разные комбинации этих икон призваны обеспечить возможность описание любой алгоритмической конструкции. Вот язык Ассемблера, из команд которого состоит любая программа. Есть такая команда, как пауза, она обеспечивает задержку в цикле активного ожидания.

Невозможность расширения возможностей путём создания новых структурных элементов есть причина узкой специализации языка ДРАКОНа.

Ещё одна причина в том, что есть последовательные алгоритмы, которые легче описывать одномерными структурами. Блок-схема такого алгоритма имеет вид цепочки прямоугольников, на концах которой два терминальных элемента: заглавие и конец. И таких алгоритмов много, особенно в системном программировании. Использование графической нотации в данном случае нецелесообразно, поскольку ведет к чрезмерной избыточности действий, связанных с созданием и расположением новых блоков. Этого, конечно можно избежать, упростив процедуру создания и размещения блока, но читаемость алгоритма будет страдать из-за больших расстояний между операторами, и как следствие алгоритм может занять не одну страницу своей блок-схемой.

Есть ещё причина, которая обусловлена самой парадигмой двумерного структурного программирования. Она проявляется, когда в коде необходимо исправить программную ошибку. В чём легче найти ошибку, в тексте или в картинке? Если в тексте, то человек просматривает его строка за строкой и его внимание равномерно распространяется на каждый оператор. А в случае с чертежом из блок-схем глаза разбегаются в разные стороны и внимание неравномерно рассеивается на разные операторы алгоритма. Однако, все верно, можно просматривать алгоритм последовательно, не пропуская ни одного оператора. Невелика вероятность пропустить оператор, когда просматриваешь блок-схему. В этом преимущество визуального программирования: возможность обойти все переходы и операторы при тестировании по метолу белого ящика.

Вообще, по моему мнению, двумерные алгоритмы занимают больше места, чем обычные. И поэтому визуальные языки не получили широкого распространения.

Если перевод блок-схемы в код приводит к потере очевидности, то это признак небрежного программирования [21]. При переводе в код возникло повторение условия в блоке ветвления и цикла. Вот как я обычно делаю, чтобы этого избежать:

WHILE  (конец последовательности цикла) DO
  IF (текущее состояние удовлетворяет условию поиска) THEN
    RETURN (состояние найдено)
  END
END
RETURN (состояние не найдено)

Листинг 1 — Пример алгоритма поиска элемента в списке

Как видите, никакого повторения нет. Это был пример к тому, что сравнение блок схем и кода зависит от того, как автор выразил алгоритм в коде. Таким образом, недостатки, присваиваемые традиционному структурному программированию, не имеют объективного характера.

Взгляните на блок схему и на код. Что больше? Да, конечно, блок-схемы обеспечивают наглядность алгоритма. Но, когда речь идет о крупных проектах, для описания алгоритмов используется код, т.к. он занимает меньше места на экране, что позволяет хранить большие объемы программного кода в репозиториях (напр. GIT).

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

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

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

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

Пусть классы не наследуют предметы материального мира, но сами подпрограммы, из которых они состоят, имеют нечто общее с системами. Каждая подпрограмма имеет набор входов и выходов, а также внутренние переменные. В системном анализе [23] для обозначения таких систем используется матрица вида:

(1)

В первой четверти находятся коэффициенты переменных состояния. Это матрица передаточной функции.

Выход системы вычисляется по формуле

(2)

Переменная состояния вычисляется по формуле

(3)

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

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

Схема барьера многопоточности

Рисунок 1 — Схема многопоточной программы с двумя барьерами (анимация из 20-и кадров объёмом 15 Кб)

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

На языке ассемблера код будет такой:

  MOV AX, threads       ; записать в аккумулятор общее число потоков
  LOCK INC wait         ; увеличить счетчик ожидающих потоков
  LOCK CMPXCHG wait, 0  ; сравнить счетчик с аккумулятором
  JNE idle              ; перейти вниз в случае неравенства
  MOV AX, 1             ; вернуть значение TRUE
  RET
idle:
  CMP wait, 0           ; сравнить счетчик ожидающих потоков с нулем
  JNZ idle              ; ждать пока счетчик обнулится другим потоком
  XOR AX,AX             ; вернуть значение FALSE
  RET

Листинг 2 — Алгоритм барьера многопоточности

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

Для реализации барьера используются две общие переменные: количество потоков (threads) и количество ожидающих потоков (wait). Логично, что поток должен ждать, пока количество ожидающих потоков не станет равно количеству всех потоков. Для проверки данного условия используется команда сравнения с обменом [24], которая за одну атомарную операцию сравнивает первый операнд (количество ожидающих потоков) с аккумулятором (общее количество потоков) и записывает в этот операнд значение константы.

Казалось бы, что такого, если поменять первые две строки. Запись в аккумулятор общего количества потоков требуется только для CAS в третьей строке. Если поменять строки, то будет так:

  LOCK INC wait         ; пришёл новый поток, которого ждали
  MOV AX, threads       ; и записал себе известное число
  LOCK CMPXCHG wait, 0  ; сравнил свой номер с этим числом
  JNE idle              ; если он пришёл последним, прыжка не будет
  MOV AX, 1             ; а будет выход с положительным ответом
  RET
idle:
  CMP wait, 0           ; иначе будет ждать пока прийдет последний
  JNZ idle              ; в цикле активного ожидания
  XOR AX,AX             ; а дождавшись выйтет с отрицательным ответом
  RET

Листинг 3 — Алгоритм барьера с переставленными строками

Известно, что доступ к памяти дольше чем к регистрам. Поэтому такая перестановка делает хорошую паузу между INC и CAS. Получается нехорошая ситуация, когда несколько потоков сначала увеличили счетчик ожидающих потоков, а потом первый выяснил, что потоков максимальное кол-во и вышел из барьера, обнулив счетчик ждущих потоков, а второй поток следом за ним сравнил этот ноль с кол-ом потоков и остался ждать1 первого, чтобы он его увеличил. А первый уже давно его увеличил и вышел. Это приводит к таким симптомам:

  1. процессор перегревается;
  2. потоки путают следующий барьер с предыдущим;
  3. программа не завершается.

Это подтверждение тому, что несущественное для последовательного программирования изменение порядка действий может нарушить работу многопоточной программы. Очевидно, что элементарными частями программ являются действия. А теперь вернемся к системному анализу. Подпрограммы — это системы с обратной связью и матрица такой системы описывает её передаточную функцию. Да, и теперь появляются две разновидности функций. У чистых функций, не имеющих побочных эффектов, коэффициенты нижнего ряда (1) равны нулю.

2.3 Автоматизация отладки программ

Есть баги, которые обусловлены многопоточностью, а ещё можно выделить:

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

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

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

Результатом внедрения паттерна проектирования в язык программирования является JavaScript. Прототипное ООП, положенное в его основу избавляет от необходимости реализации паттерна прототип. Smalltalk [27] — единственный язык программирования который полностью избавляет от необходимости использования порождающих паттернов проектирования. Вместо реализации абстрактной фабрики в нём предусмотрена возможность создания объекта из класса-объекта.

COM/DCOM [28] технологии предоставляют возможность создания объекта и вызов его методов используя строковое представление их названий. Иными совами, название вызываемой подпрограммы задается на этапе выполнения.

Больше всего особенностей в языке Ruby [29]. Этот последователь Smalltalk'а унаследовал от него принцип всё есть объект. Класс есть объект, который создает объекты своего класса [30]. Кроме того, этот язык сочетает в себе строковую идентификацию классов COM и объектно-ориентированный подход Smalltalk.

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

2.4 Автоматизированные IDE

Итак, мы выяснили, что язык ДРАКОНа не способен сместить центр тяжести в пользу автоматизации кодирования алгоритмов. Есть ли другие способы это сделать. Есть и не один. Есть такая технология, как IntelliSence, дописывание названия функции по первым буквам. Эту технологию приписывают Microsoft, однако такая возможность реализована в других IDE, есть также отдельная программа ctags, которая выполняет индексацию названий функций и других идентификаторов. Так что, эта автодополнение кода сместило усилия программиста?

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

Возможен случай совмещения визуального программирования и автодополнения кода. В том же ДРАКОНе внутренности геометрических фигур заполнены кодом. В этом коде содержатся имена функций, которые можно и нужно заполнять помогать дописывать автоматически.

Автоматизация кодирования алгоритмов достигается за счет совершенствования языков программирования [31]. Каждое обновление языка учитывает особенности разрабатываемого ПО на момент его выхода. Новый язык призван упростить те участки кода существующего ПО, которые чересчур запутаны из-за нехватки выразительных средств языка. К примеру, есть в Си т.н. сигналы. И для того, чтобы определенное действие выполнялось после завершения работы программы, нужно было создать подпрограмму и передать адрес этой подпрограммы регистратору сигналов. Затем название этой подпрограммы нигде не использовалось. С появлениям стандарта 2011 года в язык добавлены анонимные функции, которые заметно упростили регистрацию обработчика сигналов.

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

Ограничен ли ресурс совершенствования выразительных средств, или что будет дальше с языками программирования. Г. Буч сказал, что языки программирования будут развиваться в сторону паттернов проектирования. Как себе представить язык программирования со встроенными паттернами? Лень писать паттерны вручную приводит к тому, что сам компилятор будет встраивать их в программу. Долой ручная писанина.

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

2.5 Автоматизация сборки программ

Почему система сама не генерирует правила сборки? Задаём ей вопрос:

А потом система должна этот файл запомнить и при повторном запросе выдавать его.

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

На самом деле такая система уже есть и называется CMake. Она генерирует Makefile из описания проекта, который содержит:

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

Давайте детально разберем процесс автоматизации сборки проектов. Проект состоит из текстовых файлов и его необходимо перевести в бинарный формат для того, чтобы исполнять на компьютере пользователя.

Для автоматизации сборки применяются императивный и декларативный подход.

Таблица 2 — Виды средств автоматизации сборки
Язык проекта Тип автоматизации
Императивный Декларативный
С, С++, Pascal Make Automake, CMake
Java Ant Maven, Gradle

Пример императивного подхода: файлы сборки для системы Make состоят из:

Для больших проектов такой подход автоматизации сборки становится слишком утомительным, поэтому мировое сообщество придумало ряд средств, автоматизирующих создания файлов с правилами сборки: Autoconf/Automake, CMake [15].

Файл описания проекта для CMake носит название CMakeLists.txt и содержит:

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

Аналогичное соответствие сложилось в системах сборки для Java. Ant преследует императивный подход описания алгоритма сборки, а Maven и Gradle — декларативную.

В системе сборки Maven описание проекта содержится в XML файлах. Gradle избавляет от необходимости использования этого избыточного формата, предлагая описывать проект на языке Groovy. Описание проекта для этих систем сборки содержит:

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

Система сборки проектов автоматизирует не только компиляцию программы, но и её установку. Так, например для Ruby библиотеки поставляются в виде архивов, которые автоматически распаковываются в директорию с библиотеками. Для создания таких архивов используется система Rake. Скрипт автоматизации сборки Rakefile включает:

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

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

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

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

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

Объектно-ориентированное программирование ничем не отличается от процедурного. Если взглянуть на исходный код почтового клиента Sulpheed написанного на процедурном языке Си, который поддерживает несколько протоколов: POP3, IMAP, NNTP. Как он работает? Для каждого протокола реализован набор операций: получение списка писем, чтение писем.

Net Framework явился результатом автоматизации технологии COM/DCOM. Если до этого нужно было вручную вбивать UUID в определения компонента, то сейчас C# это делает внутри сам. Есть возможность прикрутить компоненты COM к приложению на языке C#. На этот случай есть специальный интерфейс COM. Т.о. C# есть не что иное, как упрощённый процесс создание COM компонентов в байт коде.

Вернемся к Sulpheed. Итак, в этом почтовом клиенте при добавлении учетной записи указывается тип протокола получения почты. Каждый протокол определяет разные алгоритмы чтения писем. При этом письма в Sulpheed для каждого протокола отображаются единообразно. Как это сделано: разработчик создал таблицу указателей для каждой процедуры работы с почтовым ящиком; затем для каждого протокола реализовал набор операций и записал их указатели в таблицу. Т.о. полиморфизм был реализован средствами процедурного языка. Если бы программа была написана на C++, то эту работу выполнял бы компилятор. Вот что я называю автоматизацией программирования. Однако, почему был выбран именно язык Си? Наверно из соображений производительности.

Для протокола POP3 отличительной чертой является то, ято чтение писем сопряжено с сохранением их на компьютере пользователя. Для IMAP письма хранятся на почтовом сервере, и предоставляется полный доступ для их чтения. NNTP — это новостной протокол, и письма рассылаются по широковещательным каналам, чтение которых предоставляется без аутентификации.

Что ещё есть в автоматизации программирования, так это снипплеты. Тот же vim есть полноценная среда разработки или САПР ПО только за счет плагинов, которые к нему подключаются. Есть определенные команды, которые пользователь вводит и получает готовый фрагмент кода, который можно заполнить нужными данными.

Однако большое количество команд запомнить невозможно, поэтому популярностью пользуются также среды разработки с графическим интерфейсом. IntelliJ IDEA и целый ряд подобных ему сред разработки для разных языков программирования от компании JatBrains, вытесняют всех его предшественников на рынке IDE. И уже вытеснило все IDE для языка Java. На то есть веские причины, почему разработчики Java предпочитают IDEA вместо аналогичных Eclipse и NetBeans. Во первых, среда удобнее и пользоваться ею значительно эффективнее. Во вторых, в ней автоматизировали рутинные операции по рефакторингу кода.

CLion [33] — это единственная среда разработки для языков C|C++, которая впервые автоматизировала работу с проектами, которые используют CMake в качестве средства сборки. Эта среда разработки использует файлы CMakeLists.txt для описания проектов, что позволяет полностью автоматизировать рутинные операции добавления новых файлов исходного кода в проект. При добавлении файла исходного кода CLion автоматически обновляет CMakeLists.txt, добавляя в его список новый файл. А при обновлении этого текстового файла автоматически запускается генерация Makefile для всего проекта.

Кроме того, начиная с третьей версии CMake может сам автоматизировать внесение новых файлов исходного кода в список с помощью команды

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

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

В C++ явно не хватает интроспекции. Это хорошо прочувствовали разработчики системы объектно-релационного отображения ODB [34]. При использовании этого средства программист описывает структуру базы данных с помощью директив препроцессора. Система ODB использует эту информацию, чтобы сгенерировать процедуры записи обозначенных с их помощью структур в реляционные таблицы.

Недостатком генераторов программного кода являются устаревание стандарта, на котором генерируется код. ODB генерировал код в стандарте 2014 года. Некоторые возможности этого языка объявлены устаневшими в 2017 году. И теперь то, что генерируется системой ODB, не пригодно для использвоания с новым компилятором языка C++ . Кто возьмётся обновить генератор кода, если каждый третий год выходит новый стандарт.

Другой способ автоматической реализации метода состоит в непосредственной генерации нового кода при компиляции. Метапрограммирование на шаблонхх позволяет строить алгоритмы для компилятора. Однако языке C++ явно недостаёт интроспекции. Решает данную проблему Java со своими аннотациями. Библиотека Hibernate позволяет генерировать SQL запросы зная только название процедуры и её аргументы. Используя аннотации обозначаются поля структуры, что используется для автоматического управления базой данных.

Писать генератор кода для таких языков как Ruby не имеет смысла, поскольку в нём реализован механизм рефлексии. В компиллируемых языках такого механизма нет. Поэтому генератор кода целесообразно создавать для них.

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

Template Toolkit [35] есть средство для генерации файлов, в т.ч. и perl-скриптов по шаблону. Именно там есть вложенность, что позволяет говорить о реализации на нём модулей знаний интеллектуального САПР ПО.

Правда, российские исследователи уже задались вопросом реализации генератора UML-диаграмм [5,36]. Кроме того, есть ещё система ПРИЗ [37], которая произвела огромное впечатление на учёных.

Подведём небольшой итог тому, что сделано для автоматизации программирования.

Таблица 3 — Обзор существующих достижений в автоматизации
Мир Страны СНГ ДонНТУ
Автоматизация кодирования Scratch, Block ДРАКОН Речевой ввод
Автоматизация тестирования Фаззинг
Автоматизация сборки CMake, Gradle
Проектирование UML Rational Rose Синтез диаграмм по описанию Пр.Обл.
Базы данных ODB, Hibernate Генераторы SQL запросов
Синтез алгоритмов Lex/Yacc ПРИЗ ?

Детальнее средства автоматизации программирования описаны в постсоветских исследованиях [38,39].

3. Сущность исследования и научная новизна

Вернёмся к ООП и паттернам проектирования. Объектно-ориентированная структура программы должна удовлетворять принципам SOLID для обеспечения гибкости процесса разработки. Есть паттерны, которые удовлетворяют этим принципам, и антипаттерны, которые наоборот, противоречат им.

Пусть система изначально не знает ни одного паттерна проектирования. Она должна их вывести сама из принципов SOLID. Для использования ГА необходимо представить паттерн в двоичном коде и вывести функцию экстремума.

Функция экстремума должна показывать, насколько система удовлетворяет принципам SOLID. От выбора этой функции зависит качество оптимизационного процесса.

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

Пусть SOLIDность системы определяется по пятибальной шкале. Это сумма пяти характеристик, соответствующих каждому принципу. Определим первую характеристику

(4)

где Cm — количество классов, имеющих несколько обязанностей; C — общее количество классов в системе.

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

Я считаю, что анализ требований состоит в разбиении требований так, чтобы каждому требованию соответствовал один класс. А не то, что преподаётся по курсу АТПО.

Чуть сложнее задача определения открытости-закрытости системы. Под этим принципом понимается открытость для добавления и закрытость для изменения. Добавление новых требований приводит к созданию новых классов а не изменению существующих. Это должно обеспечить устойчивость системы к появлению ошибок.

Механизмы рефлексии поставили на конвейерную ленту программное обеспечение.

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

Тюменцев определяет достаточное условие линейности процесса разработки [41,42], как соблюдение принципов открытости-закрытости и уменьшение размера модулей.

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

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

Список источников

  1. Брукс Ф. Мифический человеко-месяц, или Как создаются программные системы / Ф. Брукс . — 2015. — 171 с. — URL: https://nsu.ru/xmlui/bitstream... .
  2. Мартин Р.К. Быстрая разработка программ. Принципы, примеры, практика: Пер. с англ. / Р.К. Мартин, Дж.В. Ньюкирк . — М. : Вильямс , 2004. — 725 с. — URL: http://www.williamspublishing.... .
  3. Страуструп Б. Язык программирования C++. 2-е изд [Электронный ресурс]. — URL: http://www.8361.ru/6sem/books/... .
  4. Pablo's SOLID Software Development [Электронный ресурс]. — 2008. — URL: https://lostechies.com/wp-cont... .
  5. Бикмуллина И.И. Автоматический синтез диаграмм классов языка UML на основе ассоциативных отношений предметной области : диссертация [Электронный ресурс]. — Казань , 2017. — URL: http://www.dslib.net/mat-obesp... .
  6. Бочкарёва Л.В. Системы автоматизации проектирования программного обеспечения. Работа в среде Rational Rose : учебно-метод. пособие для студ. спец. Программное обеспечение информационных технологий всех форм обуч. / Л.В. Бочкарёва, М.В. Кирейцев . — Мн. : БГУИР , 2006. — 38 с.
  7. Якоб Р. Интерфейсы пользователя [Электронный ресурс]. — 2017. — URL: http://masters.donntu.ru/2017... .
  8. Голованова Е. Архитектура ПО: разница между архитектурой и проктированием [Электронный ресурс]. — 2018. — URL: https://medium.com/nuances-of-... .
  9. Ермаков И.Е. Объектно-ориентированное программирование: прояснение принципов? / И.Е. Ермаков . // Объектные системы . — № 1 (1). — 2010. — С. 130–135. — URL: https://cyberleninka.ru/articl... (дата обращения: 02.12.2019) .
  10. Крамаренко А.В. Разработка пользовательских интерфейсов на GTK+ [Электронный ресурс]. — 2011. — URL: http://masters.donntu.ru/2012... .
  11. Автоматический синтез программ – что нового? [Электронный ресурс]. // RSDN . — URL: https://www.rsdn.org/forum/phi... .
  12. Средства автоматизации проектирования программного обеспечения [Электронный ресурс]. — URL: https://poznayka.org/s86065t1.... .
  13. Система автоматизации отладки программного обеспечения [Электронный ресурс]. — URL: http://www.tehprog.ru/index.ph... .
  14. Новичков А. Rational Rose для разработчиков. Часть 2 [Электронный ресурс]. — URL: http://www.interface.ru/fset.a... .
  15. Дубров Д.В. Программирование: система построения проектов CMake. Учебник для магистратуры / Д.В. Дубров . — М. : Издательство Юрайт , 2016. — 422 с. — URL: https://aldebaran.ru/author/vl... .
  16. Clark M. Pragmatic Project Automation. How to Build, Deploy, and Monitor Java Applications / M. Clark . — The Pragmatic Bookshelf , 2004. — 172 pp. — URL: http://index-of.es/Programming... .
  17. Степанченко И.В. Методы тестирования программного обеспечения: Учебное пособие / И.В. Степанченко . — Волгоград : ВолгГТУ , 2006. — 74 с. — URL: http://window.edu.ru/resource/... .
  18. Воробьёв Л.О. К вопросу о самосинтезируемости программ / Л.О. Воробьёв, А.В. Григорьев . // Материалы VI Международной научно-технической конференции Современные информационные технологии в образовании и научных исследованиях (СИТОНИ-2019) . — Донецк : ДонНТУ , 2019. — С. 256–266.
  19. Современное визуальное программирование: Google Blockly [Электронный ресурс]. — URL: http://blogerator.org/page/sov... .
  20. Воробьёв Л.О. Разработка интегрированной CASE-системы для обучения студентов программированию / Л.О. Воробьёв, В.А. Полетаев, Д.Д. Моргайлов . // Информатика, управляющие системы, математическое и компьютерное моделирование в рамках II форума Инновационные перспективы Донбасса (ИУСМКМ-2016): VII Международная научно-техническая конференция . — Донецк : ДонНТУ , 2016. — С. 163–168. — URL: http://iuskm.donntu.ru/electr... .
  21. Ермаков И.Е. Двумерное структурное программирование / И.Е. Ермаков, Н.А. Жигуненко . // Современные информационные технологии и ИТ-образование . — № 6. — 2010. — С. 452–461. — URL: https://cyberleninka.ru/articl... (дата обращения: 21.12.2019) .
  22. Визуальное программирование–будущее написание кода [Электронный ресурс]. — URL: https://www.make-info.com/visu... .
  23. Чернышов В.Н. Теория систем и системный анализ : учеб. пособие / В.Н. Чернышов, А.В. Чернышов . — Тамбов : Изд-во Тамб. гос. техн. ун-та , 2008. — 96 с. — URL: http://window.edu.ru/resource/... .
  24. Compare-and-swap [Электронный ресурс]. // Wikipedia . — 2019. — URL: https://en.wikipedia.org/wiki/... .
  25. Фаззинг, фаззить, фаззер: ищем уязвимости в программах, сетевых сервисах, драйверах [Электронный ресурс]. // Журнал Хакер . — 2010. — URL: https://xakep.ru/2010/07/19/52... .
  26. Что такое функциональное программирование? [Электронный ресурс]. // Stack Owerflow . — URL: https://ru.stackoverflow.com/q... .
  27. Кирютенко Ю.А. Объектно-ориентированное программирование. Язык Smalltalk / Ю.А. Кирютенко, В.А. Савельев . — М. : Вузовская книга , 2003. — 358 с. — URL: http://www.mmcs.sfedu.ru/jdown... .
  28. Роджерсон Д. Основы COM : 2-е изд / Д. Роджерсон . — М. : Русская Редакция , 2000. — 228 с. — URL: http://index-of.es/Programming... .
  29. Язык программирвоания Ruby [Электронный ресурс]. — URL: https://www.ruby-lang.org/ru/ .
  30. Воробьёв Л.О. Анализ специфики реализации объектного подхода в современных технологиях программирования / Л.О. Воробьёв, А.В. Григорьев . // Программная инженерия: методы и технологии разработки информационновычислительных систем (ПИИВС-2018): сборник научных трудов II научно-практической конференции (студенческая секция) . — Донецк : ДонНТУ , 2018. — Том 2. — С. 27–32. — URL: http://pi.conf.donntu.ru/coll... .
  31. Курочкин В.М. Автоматизация программирования [Электронный ресурс]. — URL: https://www.booksite.ru/fullte... .
  32. Степанова А.С. Конвергентная технология человеко-машинного взаимодействия, на основе программирования / А.С. Степанова . // Объектные системы . — № 3 (5). — 2011. — С. 9–14. — URL: https://cyberleninka.ru/articl... (дата обращения: 02.12.2019) .
  33. CLion [Электронный ресурс]. — мощный инструмент для мощного языка! , 2019. — URL: https://jetbrains.ru/products/... .
  34. Code Synthesis Tools. C++ Object Persistence with ODB [Электронный ресурс]. — 2015. — URL: https://www.codesynthesis.com/... .
  35. Wardley A. Генерация контента сайта с использованием Template Toolkit [Электронный ресурс]. — 2000. — URL: http://www.codenet.ru/webmast/... .
  36. Бикмуллина И.И. Технология автоматизированного синтеза информационных систем с помощью семантических моделей предметной области / И.И. Бикмуллина . // Открытые семантические технологии проектирования интеллектуальных систем (OSTIS-2015) . — Минск : БГУИР , 2015. — С. 445–450. — URL: https://libeldoc.bsuir.by/hand... (дата обращения: 03.12.2019) .
  37. Ильин А.В. О двух направлениях в автоматизации программирования [Электронный ресурс]. — URL: http://ubs.mtas.ru/bitrix/comp... .
  38. Вичугова А. А. Автоматизация процесса разработки программного обеспечения: методы и средства / А. А. Вичугова . // Прикладная информатика . — № 3(63). — Томск : ТПУ , 2016. — Том 11. — С. 63–75. — URL: http://earchive.tpu.ru/bitstre... .
  39. Соловьев Н.А. Системы автоматизации разработки программного обеспечения / Н.А. Соловьев, Е.Н. Чернопрудова . — Литрес , 2012. — 230 с. — URL: https://pda.litres.ru/e-n-cher... .
  40. Форд М. Роботы наступают: Развитие технологий и будущее без работы [Электронный ресурс]. — М. : Альпина нон-фикшн , 2016. — URL: https://royallib.com/book/ford... .
  41. Тюменцев Е.А. О формализации процесса разработки программного обеспечения / Е.А. Тюменцев . // Математические структуры и моделирование . — № 3(43). — 2017. — С. 96–107. — URL: https://cyberleninka.ru/articl... (дата обращения: 16.12.2019) .
  42. Тюменцев Е.А. Уточнение к статье о формализации процесса разработки программного обеспечения / Е.А. Тюменцев . // МСиМ . — № 1 (45). — 2018. — С. 144–147. — URL: https://cyberleninka.ru/articl... (дата обращения: 16.12.2019) .

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