Автор Анна Евкова
Преподаватель который помогает студентам и школьникам в учёбе.

Применение объектно-ориентированного подхода при проектировании информационной системы

Содержание:

ВВЕДЕНИЕ

Технология программирования представляет собой совокупность средств и методов, необходимых для разработки программного обеспечения. Решающее значение для специалиста в области информационных технологий и программирования имеет выбор парадигмы программирования, определяющей успешность реализации программного продукта, и соответствующего ей языка программирования. За последние 50 лет появились сотни языков, поддерживающих различные парадигмы, причем некоторые из них используют несколько парадигм (такие языки называют мультипарадигменными). Однако, несмотря на большое количество языков программирования, существует несколько действительно важных концепций программирования, и не так много языков, которые были бы актуальны на протяжении более десяти лет [1.]. Именно парадигмы программирования определяют общий способ проектирования прикладных программ. Парадигмой программирования называют используемый различными языками подход к программированию, то есть, проще говоря, набор идей и понятий, определяющих стиль написания программ. Среди основных парадигм программирования выделяют объектно-ориентированную, императивную, декларативную, структурную, функциональную и логическую. Большинство языков программирования, активно используемых в современной разработке прикладных программ, являются мультипарадигменными.

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

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

Цели данной курсовой работы – изучить основные возможности объектно-ориентированного подхода для разработки программ, а также реализовать простейшую информационную систему с применением некоторых из этих методов. Естественным итогом этих действий должна стать разработка и тестирование программы на объектно-ориентированном языке программирования (в качестве такого языка в работе был выбран язык высокого уровня C++) для заданной предметной области. Были выбраны следующие методы исследования: систематизация специальной литературы и мировых информационных ресурсов по проблеме исследования.

Для достижения обозначенных целей в курсовой работе были также поставлены следующие задачи:

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

1. Структурный подход к проектированию программ

Структурное программирование можно рассматривать как синтез таких идей, как модульность программы и нисходящее проектирование наряду с их конкретным представлением на уровне кодирования. Это способ организации программ, который облегчает их понимание, упрощает тестирование и делает более доступным изменение. Результаты показали, что использование структурного подхода вместе с другими улучшенными технологиями программирования может привести к увеличению производительности программиста и, соответственно, к снижению частоты появления ошибок в результирующем коде [10.]. Методология структурного программирования пытается решить проблемы, связанные с безусловными переходами (то есть переходами в заданную точку программы без проверки выполнения любых условий), чтобы позволить программистам следовать логике программ.

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

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

Рисунок 1 – Основные алгоритмические конструкции

1.1 Модульность

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

Одним из мотивов модульности программы в методы является подход «разделяй и властвуй», который делает разработку программ более управляемой за счет создания независимым программ из небольших простых частей. Другой мотив – повторное использование кода – использование существующих методов в качестве строительных блоков для создания новых программ. Создавать программы из готовых методов приходится чаще, чем создавать собственный код [8.]. Третья причина – избежание повторения кода. Разделение программы на осмысленные методы облегчает отладку и сопровождение программы [7.].

1.2 Нисходящий подход

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

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

Рисунок 2 – Функциональная структура программы, спроектированной с использованием нисходящего подхода

Как следует из рисунка 2, основная программа находится на уровне цели 1. Эта основная программа делится на два модуля (подцели 11 и 1S) по мере роста требований. Это главное преимущество модульного программирования. Нисходящая методология проектирования часто использует процесс, называемый поэтапным усовершенствованием, или «разделяй и властвуй», посредством которого ситуация постепенно улучшается до достижения самого низкого уровня в структурной схеме. Этот процесс также очень часто применяется к спецификации процедур более низкого уровня. Нисходящий подход часто называют другими именами – структурированный подход, составной подход, программирование с поэтапным усовершенствованием и так далее. Хотя имена различаются, в целом образуется единый подход: сначала определяется основная функция, которая должна быть выполнена, затем определяются ее подфункции, затем подфункции этих функций и т.д. Нисходящее проектирование состоит из серии шагов, определяющих функции, необходимые для решения задачи, с точки зрения самой задачи [13.].

1.3 Восходящий подход

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

2. Объектно-ориентированный подход к проектированию программ

Выбор методологии для создания программы до появления современных языков программирования (четвертого поколения) учитывал размеры программ, которые обычно были небольшими. В небольших программах наиболее распространенным оператором обычно является оператор присваивания. Однако в больших программах (более 10000 строк) наиболее распространенным оператором обычно является вызов процедуры подпрограммы. Обеспечение правильной передачи параметров в правильную подпрограмму становится серьезной проблемой. Концепция объектно-ориентированного проектирования заключалась в определении всех классов (и связанных с ними отношений и поведения), которые имеют отношение к решаемой проблеме [13.].

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

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

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

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

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

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

Абстракция данных

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

Инкапсуляция

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

Наследование

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

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

Если имеется иерархия классов, то можно рассматривать защищенные (protected) элементы класса, которые доступны для методов своего класса и его наследников.

Одиночное наследование – это случай, когда каждый производный класс может наследовать только от одного базового класса, тогда как множественное наследование – это случай, когда класс может наследовать от двух или более базовых классов. В современных объектно-ориентированных языках, таких как Java и C++, предусмотрено множественное наследование [3.].

Полиморфизм

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

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

Таблица 1 – Различия между структурной и ООП-парадигмой

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

Объектно-ориентированное программирование

Нисходящий подход

Восходящий подход

Фокус на алгоритме и инструкциях

Фокус на объектной модели

Программа делится на подмодули, функции, процедуры

Программа организована с помощью ряда классов и объектов

Функции не зависят друг от друга

Классы могут образовать иерархию

Данные и функции рассматриваются как отдельные объекты

Данные и функции – единое целое

Повторное использование методов невозможно

Возможно повторное использование методов

Используется вызов функции

Сообщения передаются объектам

Абстракция функций

Абстракция данных

Главное – алгоритм

Главное – данные

Инкапсуляция отсутствует

Данные и функциональные возможности – единая сущность

Ключевое отношение – между программистом и программой

Ключевое отношение – между программистом и пользователем

3. Проектирование информационно-справочной системы на объектно-ориентированном языке программирования

Анализ предметной области

Информационно-справочная система (ИСС) – это интегрированный набор компонентов для сбора, хранения и обработки данных, а также для предоставления информации, знаний и цифровых продуктов. Как правило, в информационно-справочных системах нет сложных алгоритмов обработки данных, а целью системы является поиск и выдача информации в удобном виде. Коммерческие фирмы и другие организации полагаются на информационно-справочные системы для осуществления и управления своей деятельностью, взаимодействия со своими клиентами и поставщиками, а также для конкуренции на рынке. Информационно-справочные системы используются для управления цепочками поставок и электронными рынками. Например, корпорации используют информационно-справочные системы для обработки финансовых счетов, управления своими человеческими ресурсами и привлечения потенциальных клиентов с помощью онлайн-акций. Многие крупные компании построены исключительно вокруг информационно-справочных систем. К ним относится Google, компания поисковых систем, которая получает большую часть своего дохода от рекламы по некоторым ключевым словам с помощью поисковых запросов в Интернете. Цифровые товары, такие как электронные книги, видеопродукция и программное обеспечение, а также онлайн-сервисы, такие как игры и социальные сети, поставляются с информационно-справочными системами. Люди полагаются на информационно-справочные системы, обычно использующие Интернет, для ведения большей части своей личной жизни: для общения, учебы, покупок, банковских операций и развлечений.

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

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

В разрабатываемом приложении пользователю необходимо:

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

Описание программы

Для функционирования данного программного обеспечения необходим компьютер с установленным на нем средством разработки программного обеспечения MS Visual Studio 2019. В программе могут обрабатываться не только данные, введенные пользователем с клавиатуры, но и текстовые документы – каждая строка такого документа содержит набор атрибутов (данные), описывающих сотрудника (фамилия и имя, знак зодиака, дата рождения). Программное средство также может выгружать созданную информацию в текстовый документ.

Разработанное приложение поддерживается на большинстве операционных систем, среди которых Windows XP, Windows Vista, Windows 7, 8, 10.

Помимо этого, программа корректно работает как на 32-разрядных системах, так и на 64-разрядных системах.

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

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

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

Руководство пользователя (оператора)

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

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

Управление работой программы осуществляется путем выбора того или иного пункта меню.

Ввод информации может осуществляться с клавиатуры.

Для выхода из программы (завершения работы программы) необходимо закрыть окно с помощью кнопки «Выход».

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

  • ПЭВМ не ниже PentiumIV;
  • Объем ОЗУ не менее 512 Мб;
  • Монитор;
  • Клавиатура;
  • Мышь.

Программа содержит следующий набор команд:

1 – Добавить в конец

2 – Вывод списка

3 – Добавить в начало

4 – Поиск

5 – Поиск по фамилии

6 – Удаление

7 – Редактировать

8 – Сохранить в файл

9 – Считать из файла

0 – Выход

Руководство программиста

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

Программа написана на языке C++с помощью среды разработки MS Visual Studio 2019.

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

Листинг 1. Класс ZNAK, описывающий сотрудника некоторого предприятия

class ZNAK

{

public:

string name; // фамилия, имя

string zodiak; // знак зодиака

int birthday[3]; // дата рождения

ZNAK(){}

void inputData()

{

cin.ignore(cin.rdbuf()->in_avail());

string name, surname;

cout << "Введите фамилию: ";

getline(cin, surname);

while (cin.fail() || isEmpty(surname))

{

cin.clear();

cin.ignore(cin.rdbuf()->in_avail());

cout << "Введите значение повторно. Введите фамилию: ";

getline(cin, surname);

}

cout << "Введите имя: ";

getline(cin, name);

while (cin.fail() || isEmpty(name))

{

cin.clear();

cin.ignore(cin.rdbuf()->in_avail());

cout << "Введите значение повторно. Введите имя: ";

getline(cin, name);

}

this->name = surname + " " + name;

this->name = std::string(RUS(this->name.c_str()));

cout << "Введите знак зодиака (строчными буквами): ";

getline(cin, this->zodiak);

this->zodiak = std::string(RUS(this->

zodiak.c_str()));

while (cin.fail() || isEmpty(this->zodiak) || zodiaks.find(this->

zodiak) == zodiaks.end())

{

cin.clear();

cin.ignore(cin.rdbuf()->in_avail());

cout << "Введите значение повторно. Введите знак зодиака (строчными буквами): ";

getline(cin, this->zodiak);

this->zodiak = std::string(RUS(this->

zodiak.c_str()));

}

cout << "День рождения: ";

cin >> this->birthday[0];

while (cin.fail() || this->birthday[0] < 1 || this->birthday[0] > 31)

{

cin.clear();

cin.ignore(cin.rdbuf()->in_avail());

cout << "Введите значение повторно. День рождения: ";

cin >> this->birthday[0];

}

cout << "Месяц рождения: ";

cin >> this->birthday[1];

while (cin.fail() || this->birthday[1] < 1 || this->birthday[1] > 12)

{

cin.clear();

cin.ignore(cin.rdbuf()->in_avail());

cout << "Введите значение повторно. Месяц рождения: ";

cin >> this->birthday[1];

}

cout << "Год рождения: ";

cin >> this->birthday[2];

while (cin.fail() || this->birthday[2] < 1900 || this->birthday[2] > 2019)

{

cin.clear();

cin.ignore(cin.rdbuf()->in_avail());

cout << "Введите значение повторно. Год рождения: ";

cin >> this->birthday[2];

}

}

void printData(ostream &os)

{

os << this->name << " " << this->zodiak << " " << this->birthday[0] << "." << this->birthday[1] << "." << this->birthday[2] << endl;

}

};

Здесь функция-член класса inputData осуществляет ввод информации о сотруднике с клавиатуры, а функция-член printData позволяет вывести данные на экран (при использовании оператора cout в качестве параметра функции) или в файл (если используется объект класса ofstream). Кроме того, обрабатываются накладываемые на используемые типы данных ограничения:

  • имя и фамилия сотрудника не могут быть пустыми или состоять только из пробельных символов;
  • название знака зодиака должно быть существующим (то есть являться элементом множества допустимых значений, которое представлено контейнером стандартной библиотеки языка set);
  • день рождения должен быть целым числом от 1 до 31, месяц – от 1 до 12, год – от 1900 до 2019.

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

Файл может быть сохранен на диске в текстовом формате в кодировке cp1251 (ANSI). Для корректного отображения кириллических символов считанных данных на экране или в сохраняемом текстовом файле (при открытии его в текстовом редакторе) используется функция RUS (листинг 10), осуществляющая перекодировку в ANSI из кодировки cp866.

Листинг 10. Функция для конвертирования символов

char *RUS(const char *str)

{

static char buf[512];

OemToCharA(str, buf);

return buf;

}

Для работы с большим объемом данных (информации о нескольких сотрудниках) используется однонаправленный линейный список, каждый узел которого состоит из поля данных (представленного объектом класса ZNAK из листинга 1) и указателя на следующий элемент списка (рисунок 3). Исходный код класса, на базе которого реализован список, представлен в листинге 2. Внутри класса также находятся функции для поиска сотрудника и вывода данных на экран или в файл.

Рисунок 3 – Схема линейного односвязного списка

Листинг 2. Класс List для работы с информацией о сотрудниках

class List

{

public:

ZNAK data;

List* next;

List(){}

void PrintList(ostream &os)

{

List* p = this;

while (p != NULL)

{

p->data.printData(os);

p = p->next;

}

}

List* FindElem(ZNAK elem)

{

List* p = this;

while (p != NULL)

{

if ((p->data.name.compare(elem.name) == 0) && (p->data.zodiak.compare(elem.zodiak) == 0)

&& (p->data.birthday[0] == elem.birthday[0]) && (p->data.birthday[1] == elem.birthday[1])

&& (p->data.birthday[2] == elem.birthday[2]))

break;

p = p->next;

}

return p;

}

void FindElem(string sur)

{

List* p = this;

bool f = false;

while (p != NULL)

{

string surname = p->data.name.substr(0, p->

data.name.find(" "));

if (surname.compare(sur) == 0)

{

f = true;

p->data.printData(cout);

}

p = p->next;

}

if (!f)

cout << "Данные не найдены" << endl;

}

};

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

Тестирование программы

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

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

При попытке вывести содержимое списка программа сообщит об ошибке, поскольку список пуст, после чего предложит выбрать команду меню еще раз (рисунок 5).

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

Рисунок 4 – Вид окна программы при запуске

Рисунок 5 – Вид окна программы при попытке печати пустого списка

Для того, чтобы иметь возможность манипулировать данными входного файла, необходимо считать информацию из файла в список. Рисунок 8 иллюстрирует процесс чтения файла «people.txt» с некоторыми данными о сотрудниках и добавления этих данных в список.

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

Рисунок 6 – Вид окна программы после добавления элемента в список

Рисунок 7 – Содержимое входного файла "peoples.txt"

Рисунок 8 – Вид окна программы после чтения данных из входного файла

Рисунок 9 – Вид окна программы после вывода списка сотрудников на экран

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

Вид окна программы после поиска по фамилии проиллюстрирован на рисунке 11.

Вид окна программы в процессе редактирования последней добавленной записи и после вывода отредактированного списка на экран представлен на рисунке 12. Как следует из рисунка, запись «Петров Иннокентий весы 8.10.1999» была заменена на «Абрамов Станислав стрелец 5.12.1998».

На рисунке 13 приведен вид окна программы и содержимого файла «db.txt» после ввода команды сохранения списка в этот файл.

Рисунок 10 – Вид окна программы после удаления элемента из списка и печати списка

Рисунок 11 – Вид окна программы после поиска сотрудника по фамилии

Рисунок 12 – Вид окна программы после редактирования информации о сотруднике и вывода списка на экран

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

Рисунок 13 – Вид окна программы и содержимого выходного файла после сохранения списка

ЗАКЛЮЧЕНИЕ

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

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

На объектно-ориентированном языке программирования высокого уровня C++ было разработано приложение для предметной области «Информация о сотрудниках», выполняющее ряд функций: программа позволяет вводить информацию, осуществлять поиск, модификацию и удаление данных. Предусмотрена возможность сохранения данных в файл и их чтение из файла. Программа имеет понятный консольный интерфейс, выводит все необходимые пояснения и подсказки, является законченной и удобной для использования.

В ходе работы были выполнены все поставленные задачи:

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

Таким образом, все поставленные в курсовой работе цели и задачи были выполнены в полном объеме.

СПИСОК ИСПОЛЬЗОВАННЫХ ИСТОЧНИКОВ

        1. Кнут Д. Искусство программирования // The art of computer programming: [в 3 т.]. Т.1. Основные алгоритмы / Д. Кнут; ред. Ю. В. Козаченко. - 3-е изд. - М.: Вильямс, 2014. - 720 с.
        2. Оберг Р.Д., Торстейнсон П. Архитектура .NET и программирование с помощью Visual C++ / Р.Д. Оберг, П. Торстейнсон пер.с англ. М.:Издательский дом "Вильям", 2002. - 656 с.
        3. Страуструп Б. Язык программирования C++: специальное издание / Б. Страуструп; пер.: С. Анисимов, М. Кононов; ред.: Ф. Андреев, А. Ушаков. - [Б. м.]: Бином-Пресс, 2008. - 1098 с.
        4. Шилдт Г. Полный справочник по С, 4-ое издание / Г. Шилдт пер. с англ. – М.: Издательский дом «Вильямс», 2015. – 704 с.
        5. Abott, R. Program Design by Informal English Descriptions, CACM, 1993, Vol. 26 No. 11, pp. 892 - 894.
        6. Biddle, R. L., Tempero, E. D., Teaching C++ Experience at Victoria University of Wellington, 1994, In Proceedings of Software Education Conference, Dunedin, New Zealand, pp. 274 - 281.
        7. Champeaux, D. D., Panel: Structured Analysis and Object-Oriented Analysis, ECOOP/OOPSLA ’90 Proceedings, pp. 135 - 139.
        8. Deitel, P. J., Deitel, H. M., Java – How to Program, USA, Pearson Inc., 7th Ed., 2007, pp. 421 - 423.
        9. Hubbard, J. R., Programming with C++ Schaum’s Outlines, New York, 2005, McGraw-Hill Companies, Inc., pp. 273 – 299
        10. Istatkova, G. Algebra of Algorithms in Procedural and Object-Oriented, structured Programming, Automatica & Informatics, 2001, Vol.3, No. 4, pp. 56 - 62.
        11. Louden, K. C., Programming Languages: Principles and Practice, Boston, 1993, PWS Publishing Company, pp. 300 - 345.
        12. Owolabi, O., Ndeekor, C. B. Structured Programming with Pascal, Aba, 2005, Granite Ventures Nig. Ltd, pp. 19 - 21.
        13. Pressman, R. S., Software Engineering: A Practitioner’s Approach, New York, 2005, McGrawHill International 6th Ed., pp. 217 - 218.

ПРИЛОЖЕНИЕ 1. ИСХОДНЫЙ КОД ПРОГРАММЫ

//Работа с линейным однонаправленным списком

#define _CRTDBG_MAP_ALLOC

#include <stdlib.h>

#include <crtdbg.h>

#ifdef _DEBUG

#ifndef DBG_NEW

#define DBG_NEW new ( _NORMAL_BLOCK , __FILE__ , __LINE__ )

#define newDBG_NEW

#endif

#endif

#include <iostream>

#include <string>

#include <set>

#include <iomanip>

#include <sstream>

#include <fstream>

#include <windows.h>

using namespace std;

// вспомог. функция для проверки строки на пустоту

bool isEmpty(const string& str) { return str.find_first_not_of(' ') == str.npos || str.empty(); }

// вспомог. функция для перекодировки из cp866 в ANSI (при сохранении в файл и выводе на экран)

char *RUS(const char *str)

{

static char buf[512];

OemToCharA(str, buf);

return buf;

}

// множество, содержащее названия знаков зодиака

set<string> zodiaks = { "овен", "телец", "близнецы", "рак", "лев", "дева", "весы",

"скорпион", "стрелец", "козерог", "водолей", "рыбы" };

class ZNAK

{

public:

string name; // фамилия, имя

string zodiak; // знак зодиака

int birthday[3]; // дата рождения

ZNAK() {}

// Ввод данных - фамилия, имя, знак зодиака и дата рождения

void inputData()

{

// проверки добавляются в соответствии с заданной предметной областью

cin.ignore(cin.rdbuf()->in_avail());

string name, surname;

cout << "Введите фамилию: ";

getline(cin, surname);

while (cin.fail() || isEmpty(surname))

{

cin.clear();

cin.ignore(cin.rdbuf()->in_avail());

cout << "Введите значение повторно. Введите фамилию: ";

getline(cin, surname);

}

cout << "Введите имя: ";

getline(cin, name);

while (cin.fail() || isEmpty(name))

{

cin.clear();

cin.ignore(cin.rdbuf()->in_avail());

cout << "Введите значение повторно. Введите имя: ";

getline(cin, name);

}

this->name = surname + " " + name;

this->name = std::string(RUS(this->name.c_str()));

cout << "Введите знак зодиака (строчными буквами): ";

getline(cin, this->zodiak);

this->zodiak = std::string(RUS(this->zodiak.c_str()));

while (cin.fail() || isEmpty(this->zodiak) || zodiaks.find(this->zodiak) == zodiaks.end())

{

cin.clear();

cin.ignore(cin.rdbuf()->in_avail());

cout << "Введите значение повторно. Введите знак зодиака (строчными буквами): ";

getline(cin, this->zodiak);

this->zodiak = std::string(RUS(this->zodiak.c_str()));

}

cout << "День рождения: ";

cin >> this->birthday[0];

while (cin.fail() || this->birthday[0] < 1 || this->birthday[0] > 31)

{

cin.clear();

cin.ignore(cin.rdbuf()->in_avail());

cout << "Введите значение повторно. День рождения: ";

cin >> this->birthday[0];

}

cout << "Месяц рождения: ";

cin >> this->birthday[1];

while (cin.fail() || this->birthday[1] < 1 || this->birthday[1] > 12)

{

cin.clear();

cin.ignore(cin.rdbuf()->in_avail());

cout << "Введите значение повторно. Месяц рождения: ";

cin >> this->birthday[1];

}

cout << "Год рождения: ";

cin >> this->birthday[2];

while (cin.fail() || this->birthday[2] < 1900 || this->birthday[2] > 2019)

{

cin.clear();

cin.ignore(cin.rdbuf()->in_avail());

cout << "Введите значение повторно. Год рождения: ";

cin >> this->birthday[2];

}

}

// вспомог. функция для вывода одной записи в поток os

void printData(ostream &os)

{

os << this->name << " " << this->zodiak << " " << this->birthday[0]

<< "." << this->birthday[1] << "." << this->birthday[2] << endl;

}

};

class List

{

public:

ZNAK data; // информационное поле (данные) узла списка

List* next; // указатель на следующий узел списка

List(){}

// Вывод элементов списка на экран или в файл

void PrintList(ostream &os)

{

List* p = this;

while (p != NULL)

{

p->data.printData(os);

p = p->next;

}

}

// Поиск элемента в списке по имени, знаку зодиака, дате рождения

List* FindElem(ZNAK elem)

{

List* p = this;

while (p != NULL)

{

if ((p->data.name.compare(elem.name) == 0) && (p->data.zodiak.compare(elem.zodiak) == 0)

&& (p->data.birthday[0] == elem.birthday[0]) && (p->data.birthday[1] == elem.birthday[1])

&& (p->data.birthday[2] == elem.birthday[2]))

break;

p = p->next;

}

return p;

}

// Поиск элемента в списке по фамилии

void FindElem(string sur)

{

List* p = this;

bool f = false;

while (p != NULL)

{

string surname = p->data.name.substr(0, p->data.name.find(" "));

if (surname.compare(sur) == 0)

{

f = true;

p->data.printData(cout);

}

p = p->next;

}

if (!f)

cout << "Данные не найдены" << endl;

}

};

// Редактирование списка

void UpdateElem(List** ptr, ZNAK r2)

{

List* p = *ptr;

p->data.birthday[0] = r2.birthday[0];

p->data.birthday[1] = r2.birthday[1];

p->data.birthday[2] = r2.birthday[2];

p->data.name = r2.name;

p->data.zodiak = r2.zodiak;

}

// Добавление элемента в конец списка

void AddElem(List** begin, List** cur, ZNAK elem)

{

List* p = new List;

p->data = elem; //проверка, является ли список пустым

if (*begin == NULL)

{

p->next = NULL;

*begin = p;

}

else

{

p->next = (*cur)->next;// или p->next = NULL;

(*cur)->next = p;

}

*cur = p;

}

// Добавление элемента в начало списка

void AddFirstElem(List** begin, ZNAK elem)

{

List* p = new List;

p->data = elem; //проверка, является ли список пустым

if (*begin == NULL)

{

p->next = NULL;

}

else

{

p->next = *begin;

}

*begin = p;

}

// Удаление элемента из списка

void DelElem(List** begin, List* ptrCur)

{

List* p;

if (ptrCur == *begin)

{

// удаляем первый элемент

*begin = (*begin)->next;

}

else

{

// устанавливаем вспомогательный указатель на элемент, предшествующий удаляемому

p = *begin;

while (p->next != ptrCur)

p = p->next; // удаление элемента

p->next = ptrCur->next;

}

delete ptrCur;

}

// Очистка памяти

void Free(List** begin)

{

if (*begin == 0)

return;

List* p = *begin;

List* t;

while (p)

{

t = p;

p = p->next;

delete t;

}

*begin = NULL;

}

int main()

{

List* head = NULL;

List* cur = NULL;

setlocale(LC_CTYPE, "Russian");

int n = -1;

ZNAK r;

// Меню пользователя

while (n != 0)

{

cout << endl << "1 - Добавить в конец" << endl <<

"2 - Вывод списка" << endl <<

"3 - Добавить в начало" << endl <<

"4 - Поиск" << endl <<

"5 - Поиск по фамилии" << endl <<

"6 - Удаление" << endl <<

"7 - Редактировать" << endl <<

"8 - Сохранить в файл" << endl <<

"9 - Считать из файла" << endl <<

"0 - Выход\nВыберите действие: ";

cin.clear();

cin.ignore(cin.rdbuf()->in_avail());

cin >> n;

while (cin.fail() || (n < 0) || (n > 9))

{

cin.clear();

cin.sync();

cout << "Ошибка ввода, выберите действие: ";

cin >> n;

}

switch (n)

{

case 1:

{

r.inputData();

AddElem(&head, &cur, r);

break;

}

case 2:

{

if (head)

head->PrintList(cout);

else

cout << "Нет данных!" << endl;

break;

}

case 3:

{

r.inputData();

AddFirstElem(&head, r);

break;

}

case 4:

{

if (!head)

{

cout << "Нет данных!" << endl;

break;

}

List* ptr;

r.inputData();

ptr = head->FindElem(r);

if (ptr == NULL)

cout << "Запись не найдена!" << endl;

else

ptr->data.printData(cout);

break;

}

case 5:

{

if (!head)

{

cout << "Нет данных!" << endl;

break;

}

string surname;

cin.clear();

cin.ignore(cin.rdbuf()->in_avail());

cout << "Введите фамилию: ";

getline(cin, surname);

surname = std::string(RUS(surname.c_str()));

head->FindElem(surname);

break;

}

case 6:

{

if (!head)

{

cout << "Нет данных!" << endl;

break;

}

List* ptr;

r.inputData();

ptr = head->FindElem(r);

if (ptr == NULL)

cout << "Запись не найдена!" << endl;

else

{

DelElem(&head, ptr);

cout << "Запись удалена!" << endl;

};

break;

}

case 7:

{

if (!head)

{

cout << "Нет данных!" << endl;

break;

}

List* ptr;

r.inputData();

ptr = head->FindElem(r);

if (ptr == NULL)

cout << "Запись не найдена!" << endl;

else

{

ZNAK r2;

r2.inputData();

UpdateElem(&ptr, r2);

cout << "Запись отредактирована!" << endl;

};

break;

}

case 8:

{

string fname;

cin.clear();

cin.ignore(cin.rdbuf()->in_avail());

cout << "Введите имя файла для сохранения списка: ";

getline(cin, fname);

ofstream file;

file.open(fname);

while (!file.is_open())

{

cin.clear();

cin.ignore(cin.rdbuf()->in_avail());

cout << "Невозможно открыть файл для сохранения списка! Введите имя файла для сохранения списка: ";

getline(cin, fname);

}

if (head)

{

head->PrintList(file);

cout << "Список сохранен в файл!" << endl;

}

else

file << "Нет данных!" << endl;

file.close();

break;

}

case 9:

{

string fname;

cin.clear();

cin.ignore(cin.rdbuf()->in_avail());

cout << "Введите имя файла для открытия списка: ";

getline(cin, fname);

ifstream file;

file.open(fname);

while (!file.is_open())

{

cin.clear();

cin.ignore(cin.rdbuf()->in_avail());

cout << "Невозможно открыть файл для открытия списка! Введите имя файла для открытия списка: ";

getline(cin, fname);

}

char dot;

string s;

ZNAK r;

while (getline(file, s))

{

istringstream ss(s);

string surname, name, zodiak;

int b1, b2, b3;

if (ss >> surname >> name >> zodiak >> b1 >> dot >> b2 >> dot >> b3)

{

name = string(RUS(surname.c_str())) + " " + string(RUS(name.c_str()));

zodiak = string(RUS(zodiak.c_str()));

if (!isEmpty(surname) && !isEmpty(name) && !isEmpty(zodiak) && zodiaks.find(zodiak) != zodiaks.end()

&& (b1 >= 1) && (b1 <= 31) && (b2 >= 1) && (b2 <= 12) && (b3 >= 1900) && (b3 <= 2019))

{

r.name = name;

r.zodiak = zodiak;

r.birthday[0] = b1;

r.birthday[1] = b2;

r.birthday[2] = b3;

AddElem(&head, &cur, r);

}

}

}

cout << "Список получен из файла!" << endl;

file.close();

break;

}

}

}

Free(&head);

_CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);

_CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDOUT);

_CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE);

_CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDOUT);

_CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE);

_CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDOUT);

_CrtDumpMemoryLeaks();

system("pause");

return 0;

}