Этапы разработки программного продукта 2

Этапы разработки программных продуктов.

1.Кодирование программы.

Написание технического задания 

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

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

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

Кодирование — процесс написания программного кода, скриптов, с целью реализации определённого алгоритма на определённом языке программирования.

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

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



1.1. Понятие кодирования и декодирования.

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

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





1.2. Процесс кодирования.

Процесс кодирования информации может производиться либо ручным , либо автоматическим способом. При ручном , неавтоматическом способе кодирования вручную отыскивается нужный код в предварительно составленном каталоге кодов и записывается в документе в виде цифровых или алфавитно-цифровых символов. Затем документ поступает в вычислительный центр , где оператор с помощью клавишного устройства перфорирует записанную информацию на перфокарте или перфоленте. Затем перфокарты или перфоленты вводятся в ЭВМ , информация кодируется в машинный (двоичный) код. Таким образом информация дважды кодируется вручную : при записи ее на документ и при переноски данных на машинные носители. При автоматическом способе кодирования человек производит запись на естественном языке в виде слов, цифр и общепринятых обозначений в документе , который читается специальным автоматом. Этот автомат предварительно кодирует документ и записывает все данные на магнитную ленту в двойном коде. Лента затем вводится в ЭВМ, где информация с помощью “машинного словаря “ снова кодируется в более короткий машинный код, удобный для ее поиска, сортировки и обработки. Ввод информации в ЭВМ в виде буквенно-цифрового текста на естественном языке и кодировании в машине требует хранения в памяти ЭВМ словаря, в котором каждому слову соответствует определенный код.По этому словарю машина сама кодирует текст. При этом отпадает необходимость в классификации и кодировании информации по ее смысловому содержанию, так как котируются сами слова, выражающие определенные характеристики предметов. Большое разнообразие технических характеристик и других данных, относящихся к производству и потреблению многочисленных видов продукции, не позволяет включить все необходимые данные для их производства в код продукции, так как этот код содержал бы большое число символов. Поэтому задача кодирования продукции заключается в том, чтобы иметь возможно более короткий код, по которому в памяти машины можно было бы найти подробную информацию о всех необходимых данных, относящихся к каждому изделию. Таким кодом является ключевой код.



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





















1.3. Виды кодов.

Код , символы которого соответствуют определенным предметам или характеристикам , называется прямым кодом . Если код непосредственно не содержит информацию о предмете или его признаках , а представляет адрес , указывающий местоположение информации , где содержится необходимые сведения , то он называется адресным кодом. Адресный код применяется для сокращения кода и быстрого поиска больших массивов информации. За единицу количества информации принимается 1 бит, т.е. один двоичный разряд (0 или 1). Буквы, десятичные цифры и другие символы внутри ЭВМ представляются в виде групп двоичных разрядов. Операция представления их в таком виде называется двоичным кодированием. Группа из n двоичных чисел позволяет закодировать 2n различных символов. Такая группа называется байтом.

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

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







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































2. Тестирование.

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

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

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

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



2.1.Тестирование программного обеспечения.





Многие организации, занимающиеся созданием программного обеспечения, до 50% средств, выделенных на разработку программ, тратят на тестирование, что составляет миллиарды долларов по всему миру в целом. И все же, несмотря на громадные капиталовложения, знаний о сути тестирования явно не хватает и большинство программных продуктов неприемлемо ненадежно даже после «основательного тестирования».

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

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

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

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

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

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

Тестирование оказывается довольно необычным процессом (вот почему оно и считается трудным), так как этот процесс разрушительный. Ведь цель проверяющего— заставить программу сбиться. Он доволен, если это ему удается; если же программа на его тесте не сбивается, он не удовлетворен.

Еще одна причина, по которой трудно говорить о тестировании — это тот факт, что о нем известно очень немногое. Если сегодня мы располагаем 5% тех знании о проектировании и собственно программировании (кодировании), которые будут у нас к 2000 г., то о тестировании нам известно менее 1%



2.2.  Основные определения.



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

Тестирование (testing), как мы уже выяснили,—процесс выполнения программы (или части программы) с намерением (или целью) найти ошибки.

Доказательство (proof) — попытка найти ошибки в программе безотносительно к внешней для программы среде. Большинство методов доказательства предполагает формулировку утверждений о поведении программы и затем вывод и доказательство математических теорем о правильности программы. Доказательства могут рассматриваться как форма тестирования, хотя они и не предполагают прямого выполнения программы. Многие исследователи считают доказательство альтернативой тестированию — взгляд во многом ошибочный; более подробно это обсуждается в гл. 17.

Контроль (verification) — попытка найти ошибки, выполняя программу в тестовой, или моделируемой, среде.

Испытание (validation) — попытка найти ошибки, выполняя программу в заданной реальной среде.

Аттестация (certification) — авторитетное подтверждение правильности программы, аналогичное аттестации электротехнического оборудования Underwriters Laboratories. При тестировании с целью аттестации выполняется сравнение с некоторым заранее определенным стандартом.

Отладка (debugging) не является разновидностью тестирования. Хотя слова «отладка» и «тестирование» часто используются как синонимы, под ними подразумеваются разные виды деятельности. Тестирование — деятельность, направленная на обнаружение ошибок; отладка направлена на установление точной природы извесной ошибки, а затем — на исправление этой ошибки. Эти два вида деятельности связаны — результаты тестирования являются исходными данными для отладки.

Тестирование модуля, или автономное тестирование (module testing, unit testing) — контроль отдельного программного модуля, обычно в изолированной среде (т. е. изолированно от всех остальных модулей). Тестирование модуля иногда включает также математическое доказательство.

Тестирование сопряжении (integration testing) — контроль сопряжении между частями системы (модулями, компонентами, подcистемами).

Тестирование внешних функций (external function testing) — контроль внешнего поведения системы, определенного внешними спецификациями.

Комплексное тестирование (system testing) — контроль и/или испытание системы по отношению к исходным целям. Комплексное тестирование является процессом контроля, если оно выполняется в моделируемой среде, и процессом испытания, если выполняется в среде реальной, жизненной.

Тестирование приемлемости (acceptance testing) — проверка соответствия программы требованиям пользователя.

Тестирование настройки (installation testing) — проверка соотетствия каждого конкретного варианта установки системы с целью выявить любые ошибки, возникшие в процессе настройки системы.

Отношения между этими типами тестов и проектной документацией, на которой основывается тест.



2.3. Философия тестирования



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

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

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

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

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





2.4	Интеграция модулей.



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

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





2.1.1. Восходящее тестирование.



При восходящем подходе программа собирается и тестируется снизу вверх. Только модули самого нижнего уровня («терминальные» модули; модули, не вызывающие других модулей) тестируются изолированно, автономно. После того как тестирование этих модулей завершено, вызов их должен быть так же надежен, как вызов встроенной функции языка или оператор присваивания. Затем тестируются модули, непосредственно вызывающие уже проверенные. Эти модули более высокого уровня тестируются не автономно, а вместе с уже проверенными модулями более низкого уровня. Процесс повторяется до тех пор, пока не будет достигнута вершина. Здесь завершаются и тестирование модулей, и тестирование сопряжении программы.

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



2.1.2. Нисходящее тестирование.



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

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

Интересен и второй вопрос: в какой форме готовятся тестовые данные и как они передаются программе? Если бы головной модуль содержал все нужные операции ввода и вывода, ответ был бы прост:

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

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

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

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

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

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



2.1.3. Модифицированный нисходящий метод



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

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

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

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





2.1.4.	Метод большого скачка.



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

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

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











2.1.5. Бета - тестирование программного обеспечения.

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



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







































3. Немного из теории справочных систем


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

Давать полное описание по вопросам использования программы.

Иметь графические материалы по вопросам использования программы.

Быть доступной для вызова из любой формы программы.

Иметь контекстные описания и удобную систему поиска информации.

Иметь минимально возможный размер.


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









Тестирование и принятие в эксплуатацию



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



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



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

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

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



Требования к документации



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



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



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



Внедрение и сопровождение разработанной программной системы



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



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



Замена версий и управление конфигурацией

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



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



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



Процедуры запросов на замену версий



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



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



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



Управление конфигурацией и настройки защиты



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



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



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









Управление конфигурацией и обновление



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



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



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



Тестирование перед инсталляцией



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



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



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

Создание документации пользователя

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

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

Типы документации

Существует четыре основных типа документации на ПО:

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

техническая — документация на код, алгоритмы, интерфейсы, API

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

маркетинговая

Архитектурная/проектная документация



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

Техническая документация



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

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



Часто при составлении технической документации используются автоматизированные средства — генераторы документации, такие как Doxygen, javadoc, NDoc и другие. Они получают информацию из специальным образом оформленных комментариев в исходном коде, и создают справочные руководства в каком-либо формате, например, в виде текста или HTML.



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

Пользовательская документация



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



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



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



Существует три подхода к организации пользовательской документации. Вводное руководство (англ. tutorial), наиболее полезное для новых пользователей, последовательно проводит по ряду шагов, служащих для выполнения каких-либо типичных задач. Тематический подход, при котором каждая глава руководства посвящена какой-то отдельной теме, больше подходит для совершенствующихся пользователей. В последнем, третьем подходе, команды или задачи организованы в виде алфавитного справочника — часто это хорошо воспринимается продвинутыми пользователями, хорошо знающими, что они ищут. Жалобы пользователей обычно относятся к тому, что документация охватывает только один из этих подходов, и поэтому хорошо подходит лишь для одного класса пользователей.



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

Маркетинговая документация



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

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

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

объяснить положение продукта по сравнению с конкурирующими решениями



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



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

Метрика программного обеспечения (англ. software metric) — это мера, позволяющая получить численное значение некоторого свойства программного обеспечения или его спецификаций.

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

Обобщённое программирование ( Шаблоны ) — парадигма программирования, заключающаяся в таком описании данных и алгоритмов, которое можно применять к различным типам данных, не меняя само это описание. В том или ином виде поддерживается разными языками программирования. Возможности обобщённого программирования впервые появились в 70-х годах в языках CLU и Ada, а затем во многих объектно-ориентированных языках, таких как C++, Java, Object Pascal[1], D и языках для платформы .NET.



Общий механизм



Средства обобщённого программирования реализуются в языках программирования в виде тех или иных синтаксических средств, дающих возможность описывать данные (типы данных) и алгоритмы (процедуры, функции, методы), параметризуемые типами данных. У функции или типа данных явно описываются формальные параметры-типы. Это описание является обобщённым и в исходном виде непосредственно использовано быть не может.



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



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

Способы реализации



Известно два основных способа реализации поддержки обобщённого программирования в компиляторе.

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

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