Главная страница
Навигация по странице:

  • 2 Разработка программного обеспечения

  • Проектирование программного обеспечения

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

  • Программа

  • Конец-если

  • Курс лекций инструментальные средства. Конспект лекций


    Скачать 1.6 Mb.
    НазваниеКонспект лекций
    АнкорКурс лекций инструментальные средства
    Дата22.07.2021
    Размер1.6 Mb.
    Формат файлаdoc
    Имя файлаkurs_lekciy_isrpo.doc
    ТипКонспект
    #225070
    страница2 из 8
    1   2   3   4   5   6   7   8

    Контрольные вопросы

    1. Что такое жизненный цикл программного обеспечение?

    2. Перечислите основные модели процесса разработки программного обеспечения?

    3. Каковы основные свойства каскадной (итерационной) модели жизненного цикла?

    4. Из каких этапов состоит модель жизненного цикла UML?


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

    Методология Extreme Programming (XP) бросила вызов многим устоявшимся представлениям о разработке программного обеспечения. Пожалуй, наиболее противоречивой идеей является отказ от предварительного проектирования в пользу более эволюционного Подхода. Противники ХР считают, что это возврат к разработкам типа «code and fix» («пишем и правим»). Для приверженцев же новой методологии это отказ от техник проектирования (например, UML), их принципов. Незачем беспокоиться о проектировании, считают они. Достаточно внимательно «вслушиваться» в свой код, и проектирование образуется само собой.

    В большинстве случаев эволюционное проектирование — это нечто ужасное. В конце концов, все равно вместо дизайна системы вы получаете просто набор из специфических решений, каждое из которых затрудняет дальнейшие изменения в программном коде. Часто это вообще нельзя считать дизайном (и, уж конечно, такой дизайн никак нельзя назвать хорошим). Как говорит Кент, дизайн существует для того, чтобы дать возможность оперативно вносить в систему любые изменения. Если дизайн плох, то такая возможность исчезает. В результате вы будете иметь дело с энтропией программного продукта, и со временем и без того плохой дизайн системы станет еще хуже. Теперь вам будет не только сложнее вносить в систему изменения, но и отыскивать и исправлять ошибки, которые начинают множиться с катастрофической быстротой. Все это — кошмар разработок в стиле «code and fix», когда с течением времени исправление ошибок обходится все дороже и дороже.

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

    Такой подход к разработке ПО не нов — им активно пользуется множество людей начиная с 1970-х годов. По многим показателям он гораздо лучше, чем эволюционное проектирование в стиле «code and fix», однако и у него есть существенные недостатки. Один из главных недостатков заключается в том, что невозможно заранее продумать все вопросы, с которыми придется столкнуться во время кодирования системы. Таким образом, в ходе работ непременно возникнет ситуация, когда у программистов появятся вопросы относительно спроектированного дизайна. А что, если проектировщики, закончив свою часть работы, уже переключились на другой проект? Тогда программисты начинают самостоятельно решать сложившуюся проблему, отступая от уже принятых проектных решений и внося при этом в Программный продукт долю энтропии. И даже если проектировщик еще работает над проектом и может помочь, все равно ему потребуется довольно много времени, чтобы выяснить ситуацию, внести изменения в диаграммы и уже затем менять код. Л при разработке, как правило, вопрос времени всегда стоит остро. Отсюда энтропия (опять-таки).

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

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

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

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

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

    Структурная схема разрабатываемого программного обеспечении

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

    Структурная схема определяется архитектурой разрабатываемого ПО.

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

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

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

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

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


    Рисунок 2.1 - Пример структурной схемы программного комплекса
    Функциональная схема

    Функциональная схема (ГОСТ 19.701—90) — это схема взаимодействия компонентов программного обеспечения с описанием информационных потоков, состава данных в потоках и указанием используемых файлов и устройств. Для изображения функциональных схем используют специальные обозначения, установленные стандартом (таблица 2.1).
    Таблица 2.1 - Обозначения элементов функциональных схем



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


    Рисунок 2.2 - пример функциональной схемы программного комплекса
    Метод пошаговой детализации при составлении алгоритмов

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

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

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

          3. На этом и последующих уровнях в виде последовательных итераций производятся те же действия, что описаны на этапе 2.

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

          1. Разработка завершена: в модульном виде получено описание требуемой программы. Перевод этого описания в программу на конкретном языке программирования должен быть достаточно простой задачей.

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

    Методика структурных карт используется на этапе проектирования ПО для того, чтобы продемонстрировать, каким образoм программный продукт выполняет системные требования. При этом наиболее часто применяются две техники: структурные карты Константайна (Constantine), предназначенные для описания отношений между модулями, и структурные карты Джексона (Jackson), предназначенные для описания внутренней структуры модулей.

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

    • модуль имеет имя, по которому к нему можно обращаться как к единому фрагменту;

    • модуль состоит из множества операторов языка программирования, записанных последовательно;

    • модуль может принимать и/или передавать данные как параметры в вызывающей последовательности или связывать данные через фиксированные ячейки или общие области.

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



    Рисунок 2.3 - Элементы структурных карт:

    а — модуль; б— вызов модуля; в— связь по данным; г— связь по управлению
    Модуль является базовым элементом структурной карты. Различают следующие типы модулей (рисунок 2.4):

    • модуль (рисунок 2.4, а);

    • подсистема — детализированный модуль или программа. Может использоваться повторно любое число раз (рисунок 2.4, б);

    • библиотека — совокупность подпрограмм, размещенных в модуле отдельно отданной системы (рисунок 2.4, в);

    • область данных — описывает модули, содержащие исключительно области глобальных/распределенных данных (рисунок 2.4, г).



    Рисунок 2.4 – Типы модулей
    Отдельные части программной системы (программы, подпрограммы) могут вызываться последовательно, параллельно или как сопрограммы (рисунок 2.5),



    Рисунок 2.5 – Типы вызовов модулей
    Для моделирования условных и циклических вызовов применяются следующие узлы (рисунок 2.6):

    • условный узел применяется для моделирования конструкций IF-THEN-ELSE (на диаграмме из узла выходят два потока) и IF-THEN (из узла выходит один поток). Условный узел изображается в виде ромба, потоки — альтернативные вызовы — изображаются выходящими из него;

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



    Рисунок 2.6- Условные и циклические вызовы модулей:

    а - циклический; б— условный; воднократный
    Если необходимо показать, что подчиненный модуль вызывается однократно, это осуществляется указанием цифры «1» рядом со стрелкой, обозначающей вызов модуля-наследника.

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

    Рисунок 2.7 - Связи

    а — по данным; б- по управлению
    Пример.

    Разработать структурную карту Константайна для задачи сортировки одномерного массива с помощью алгоритмов Пузырька, прямого выбора и Шелла.

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



    Рисунок 2.8 – Пример структурной карты Константайна
    Результат приведен на рисунке 2.8.
    Структурные карты Джексона

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

    • операция — блок кодов, имеющий один вход и один выход (рисунок 2.9, а);

    • следование — последовательное выполнение операций слева направо (рисунок 2.9, б);

    • выбор — выполнение одной из операций в зависимости от выполнения условия (рисунок 2.9, в);

    • итерация — многократное выполнение блока (рисунок 2.9, г).



    Рисунок 2.9 – Элементы структурных диаграмм Джексона
    Пример.

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

    Программа

    Цикл пока не конец файла

    Прочитать запись

    Сравнить заданные поля с критерием поиска

    Если совпали

    Сохранить в выходной список

    Конец-если

    Конец-цикл

    Вывод результирующего списка

    Конец-программа



    Рисунок 2.10 – Структурная карта Джексона
    Полученная структурная карта Джексона приведена на рисунке 2.10.
    CASE-технологии

    CASE-технологии (Computer-Aided Software/System Engineering — разработка программного обеспечения/систем с использованием компьютерной поддержки) — это реализованные в виде программных продуктов технологические системы, ориентированные на создание сложных программных систем и поддержку их полною жизненною цикла или его основных этапов. В настоящее время CASE-технологии используются не только для производства ПП, но и как мощный инструмент решения исследовательских и проектных задач (структурный анализ предметной области, моделирование деловых предложений с целью решения задач оперативного и стратегического планирования и управления ресурсами).

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

    CASE-средства обладают следующими основными достоинствами:

    • повышают качество создаваемого ПО с помощью средств автоматического контроля;

    • ускоряют процесс проектирования и разработки;

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

    • освобождают разработчика от рутинной работы, частично генерируя коды программ;

    • поддерживают технологии повторного использования компонентов ПО;

    • поддерживают развитие и сопровождение разработки.

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

    Таблица 2.2 – Сравнительная характеристика этапов жизненного цикла ПО


    Наиболее просто автоматизируемыми оказались стадии «контроль проекта» и «кодогенерация», хотя все остальные этапы Жизненного цикла ПО также поддерживаются СASЕ-технологнями. Кроме изменения содержания фаз, существенно изменилось Распределение трудозатрат по фазам, как показано в таблице 2.3.

    Таблица 2.3. Распределение трудозатрат по фазам жизненного цикла ПО


    Таблица 2.4 содержит сравнительную характеристику целей и содержания этапов жизненного цикла ПО при традиционной разработке и с помощью CASE-средств.
    Таблица 2.4 – Цели и содержание этапов жизненного цикла ПО


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

    Чем же принципиально CASE -технология отличается от традиционной технологии разработки ПО? Девизом разработчиков CASE-технологий является фраза «одна картинка стоит тысячи слов». Поэтому при использовании CASE-средств функционирование объекта (разрабатываемого ПО) отражается в различных схемах, таблицах, диаграммах, картах и т. п.

    Большинство CASE-технологий основано на парадигме методология/метод/нотация/ средство.

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

    Метод определяет способ достижения той или иной цели.

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

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

    Наиболее часто и эффективно в методологии структурного анализа используются следующие средства:

    • DFD (Data Flow Diagrams) — диаграммы потоков данных совместно со словарями данных и спецификациями про-

    • ERD (Entity-Relationship Diagrams) диаграммы «сущность—связь»;

    • STD (State Transition Diagrams) — диаграммы переходов состояний.

    Современные структурные методологии анализа и проектирования классифицируются по следующим признакам:

    • по типу целевых систем — для систем реального времени и для информационных систем;

    • по отношению к школам — Software Engineering (SE) и Information Engineering (IE);

    • по порядку построения моделей — процедурно-ориентированные, ориентированные на данные и информационно-ориентированные.


    В таблице 2.5 приведены отличия информационных систем от систем реального времени.

    Таблица 2.5 – Отличия информационных систем от систем реального времени


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

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

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

    В связи с развитием CASЕ-технологий в рамках спиральной модели жизненного цикла ПО в последнее время широкое рас-

    распространение получила методология быстрой разработки приложений RAD (Rapid Application Development). Процесс разработки при этом содержит три элемента:

    • небольшую команду программистов (от 2 до 10 человек), что облегчает управление проектом;

    • короткий, но тщательно проработанный производственный график (от 2 до 6 мес.), повышает эффективность работы;

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

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

    Жизненный цикл ПО методологии RAD состоит из четырех фаз:

    • анализа и планирования требований;

    • проектирования;

    • реализации;

    • внедрения.

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

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

    По результатам анализа процессов принимается решение о количестве, составляющих ИС подсистем, поддающихся разработке одной командой разработчиков за приемлемое для RAD-проектов время — порядка 2—3 мес.

    Результатом данной фазы должны быть:

    • общая информационная модель системы;

    • функциональные модели системы в целом и подсистем, реализуемых отдельными командами разработчиков;

    • точно определенные с помощью CASE-средства интерфейсы между автономно разрабатываемыми подсистемами;

    • построенные прототипы экранов, отчетов, диалогов.

    Использование CASE-средств позволяет избежать искажения

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

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

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

    Результатом фазы является готовая система, удовлетворяющая всем согласованным требованиям.

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

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

    программ, требующих написания большого объема (сотни тысяч строк) уникального кода.

    Основные принципы методологии RAD:

    • итерационная разработка приложений;

    • необязательность полного завершении работ на каждом из этапов жизненного цикла;

    • применение CASE-средств, обеспечивающих целостность данных;

    • участие конечных пользователей впроцессе разработки ИС;

    • разработка прототипов, позволяющая полнее выяснить и удовлетворить потребности конечного пользователя;

    • тестирование, производимое параллельно с разработкой;

    • разработка подсистем несколькими немногочисленными хорошо управляемыми командами профессионалов;

    • четкое планирование и контроль выполнения работ.

    1   2   3   4   5   6   7   8


    написать администратору сайта