Учреждение высшего образования «Университет управления «ТИСБИ» Факультет информационных технологий Кафедра информационных технологий КУРСОВАЯ РАБОТА по дисциплине «Программная инженерия» на тему: Разработка объектной программы для задачи обработки данных о вкладах клиентов банка Выполнил: студент ПИ-032 Топорков М.В. Проверил: ст. преподаватель Потапова Е.А. Казань 2023 Оглавление Постановка задачи................................................................................................... 3 Описание используемых структур ........................................................................ 5 Краткие сведения об объектном подходе ............................................................. 8 Описание разработанных классов ....................................................................... 12 Описание демонстрационного модуля с характеристикой использованных стандартных компонентов и списком реализованных обработчиков ............. 14 Реализованные обработчики событий ................................................................ 16 Описание структуры проекта в соответствии с использованным инструментом разработки .................................................................................... 24 Список литературы ............................................................................................... 26 Листинг кода .......................................................................................................... 28 2 Постановка задачи Разработать объектную программу для хранения и обработки данных о вкладах клиентов банка. Программа должна вести список лицевых счетов своих клиентов с хранением фамилии клиента и текущей суммы вклада. Для каждого лицевого счета должен создаваться список выполненных с ним операций с указанием уникальной даты операции и величины изменения вклада (пополнение или снятие денежных средств). Разработка включает в себя: определение необходимых объектов и способов их взаимодействия формальное описание объектов в виде классов программную реализацию всех необходимых методов, включая подсчет всех суммарных показателей всестороннее тестирование методов с помощью консольного (при разработке) и оконного (в окончательном варианте) приложения. Для объединения клиентов используется структура данных в виде очереди на основе динамического массива со сдвигом элементов. Для объединения операций клиента используется структура данных в виде адресного разомкнутого упорядоченного однонаправленного списка без заголовка. Разработка выполняется с учетом следующих требований: имена классов, свойств и методов должны носить содержательный смысл и соответствовать информационной задаче. обязательное соблюдение принципа инкапсуляции – использование в классах только закрытых свойств и реализация необходимого набора методов доступа. 3 наличие двух методов для сохранения всей объектной структуры во внешнем файле с обратной загрузкой, при этом стандартные механизмы реализации разрешается использовать только как дополнение к самостоятельно реализованным методам. тестовое пользовательским оконное приложение интерфейсом с должно контролем обладать вводимых удобным данных и отображением текущего состояния объектной структуры с помощью списковых или табличных компонентов. стандартные контейнеры/коллекции (включая обобщенные классы) разрешается использовать только как дополнение к самостоятельно разработанным классам. в качестве языка разработки разрешается использовать Java, С#, C++, Object/Free Pascal и соответствующие инструменты быстрой разработки приложений. 4 Описание используемых структур Для разработки программы используется две структуры данных: Очередь на основе динамического массива со сдвигом элементов — это структура данных, которая позволяет хранить элементы в порядке, в котором они были добавлены в очередь, и осуществлять операции добавления нового элемента в конец очереди (enqueue) и удаления элемента из начала очереди (dequeue). Для реализации очереди на основе динамического массива со сдвигом элементов, необходимо использовать массив фиксированного размера и дополнительные переменные, указывающие на начало и конец очереди. При добавлении нового элемента в конец очереди (enqueue), сначала проверяем, есть ли свободное место в массиве. Если места достаточно, то добавляем новый элемент в конец массива и обновляем переменную, указывающую на конец очереди. Если же места не хватает, то необходимо осуществить сдвиг элементов, чтобы освободить место для нового элемента. Для этого сдвигаем все элементы очереди на одну позицию вправо, начиная с начала очереди. После сдвига элементов добавляем новый элемент в конец массива и обновляем переменную, указывающую на конец очереди. При удалении элемента из начала очереди (dequeue), сначала проверяем, пуста ли очередь. Если очередь пуста, то возвращаем ошибку. Если очередь не пуста, то удаленным элементом будет элемент, находящийся в начале очереди. После удаления элемента, необходимо сдвинуть оставшиеся элементы очереди на одну позицию влево. После сдвига обновляем переменную, указывающую на начало очереди. Такая реализация очереди на основе динамического массива со сдвигом элементов позволяет эффективно использовать память и осуществлять операции добавления и удаления элементов с постоянной временной сложностью O(1), при условии, что имеется достаточное количество свободного места в массиве. Однако, при выполнении сдвига элементов, временная сложность операции может быть O(n), где n количество элементов в очереди. 5 head tail One1 Two2 three3 Добавление (Рис.1 Добавление в очередь) head tail One1 Two2 three3 Удаление (Рис.2 Удаление из очереди) Адресный разомкнутый упорядоченный однонаправленный список без заголовка - это структура данных, представляющая собой последовательность элементов, связанных между собой посредством адресов. В таком списке каждый элемент содержит значение и ссылку на следующий элемент. Последний элемент списка будет иметь ссылку на null, обозначая конец списка. Для хранения списка без заголовка используется переменная, которая указывает на первый элемент списка. В отличие от списка с заголовком, в списке без заголовка нет отдельно описанного элемента-заголовка. Поэтому точкой входа в список будет указатель на первый элемент. Упорядоченность списка означает, что элементы в списке располагаются в порядке возрастания или убывания. Это значит, что новые элементы вставляются в нужное место, чтобы сохранить порядок. Удаление элемента из такого списка может происходить следующим образом: проверяем, не является ли список пустым (т.е. указатель на первый элемент равен null). Если да, то выводим сообщение об ошибке. Итерируемся по списку от начала до конца, сравнивая значения элементов, чтобы найти элемент, который нужно удалить. При нахождении элемента, меняем ссылку предыдущего элемента так, чтобы она указывала на следующий элемент после удаляемого элемента. Удаляем элемент из памяти. 6 Добавление нового элемента в упорядоченный список может происходить следующим образом: создаём новый элемент списка с заданным значением. Проверяем, не является ли список пустым. Если список пустой, то устанавливаем указатель на первый элемент в новый элемент списка. Если список не пустой, итерируемся по списку от начала до конца, сравнивая значения элементов, чтобы найти место для вставки нового элемента. При нахождении нужного места, меняем ссылку предыдущего элемента так, чтобы она указывала на новый элемент, а ссылка нового элемента указывала на следующий элемент после предыдущего. Если новый элемент должен быть добавлен в конец списка, то меняем ссылку последнего элемента так, чтобы она указывала на новый элемент. Таким образом, адресный разомкнутый упорядоченный однонаправленный список без заголовка позволяет хранить элементы в упорядоченном виде, а операции добавления и удаления элементов выполняются в зависимости от упорядоченности списка. One1 Two2 Three3 Four4 Next Next Next Next (Рис.3 Добавление элементов в список) One1 Two2 Three3 Four4 Next Next Next Next (Рис.4 удаление элементов в список) 7 Краткие сведения об объектном подходе Перед нами была поставлена задача, создать и разработать объектную программу. Объектный подход (или объектно-ориентированный подход) – это методологическая концепция программирования, которая основывается на использовании объектов и их взаимодействия для решения задач. В объектноориентированном программировании (ООП), каждый объект представляет собой экземпляр класса, который определяет его свойства и методы. Сведения об объектном подходе включают следующие аспекты. Классы: объекты создаются на основе классов. Класс определяет набор свойств (переменных) и методов (функций), которыми объекты будут обладать при создании. Понятие «объект» достаточно общее и может быть интерпретировано по-разному. В объектно-ориентированном программировании, объект – это сущность, которая имеет свой набор свойств, а также набор функций (поведение). Классом же является только модель пока что не существующей(их) сущности(-ей). Класс описывает устройство объектов, количество свойств, количество функциональности, поведение объекта и т.д. Объект может является только представителем какого-либо класса, и не может существовать сам по себе. Объект, созданный при помощи класса, является его экземпляром, все объекты, созданные из одного класса, имеют одинаковую структуру. Объекты являются моделями реальных предметов и сущностей, и разрабатываются для выполнение какой-либо задачи. Придерживаясь объектного подхода, необходимо соблюдать все его основные принципы, к которым относятся абстракция, инкапсуляция и наследование. Инкапсуляция — это концепция объектно-ориентированного программирования, которая позволяет объединить данные (переменные) и методы (функции), работающие с этими данными, в один контейнер, называемый классом. Основная идея инкапсуляции заключается в том, что объекты должны скрывать свою внутреннюю реализацию и предоставлять 8 только публичный интерфейс для взаимодействия с внешним миром. Для реализации инкапсуляции в объектно-ориентированном программировании используются модификаторы доступа, такие как public, private и protected. Модификатор public обозначает, что метод или переменная являются публичными и могут быть доступными из любого места в программе. Это означает, что они могут быть использованы другими классами или объектами для выполнения определенных операций. Модификатор private определяет, что метод или переменная являются закрытыми и не могут быть доступными извне класса, в котором они определены. Это означает, что они могут быть доступны только внутри класса и используются для управления внутренней реализацией объекта. Модификатор protected указывает, что метод или переменная являются защищенными и могут быть доступными только для классов-наследников или классов в том же пакете. Они не доступны извне класса, в котором они определены, и используются для обеспечения взаимодействия между связанными классами. Использование модификаторов доступа позволяет реализовать принцип инкапсуляции, позволяющий скрыть сложность и детали реализации класса, облегчить и упростить использование объектов и обеспечить безопасность данных. Когда методы и переменные класса являются закрытыми (private) или защищенными (protected), это означает, что другие классы или объекты не могут напрямую изменять или читать их значения, а должны использовать публичные методы, предоставляемые классом, что способствует контролю над состоянием объекта и предотвращению некорректного использования. Таким образом, инкапсуляция позволяет создавать более устойчивые, модульные и безопасные программы. Это обеспечивает логическую группировку данных и методов в классе, только некоторые из которых являются доступными для внешнего мира, а также предоставляет механизм контроля доступа к данным и обеспечивает устойчивость программы к изменениям внутренней реализации класса. 9 Наследование — это концепция объектно-ориентированного программирования, которая позволяет создавать новые классы на основе существующих классов, называемых базовыми классами или родительскими классами. Класс-потомок наследует все атрибуты (переменные) и методы базового класса, что позволяет повторно использовать код и упрощает разработку и поддержку программного кода. Одной из основных целей наследования является создание иерархии классов с различными уровнями абстракции. Например, у нас может быть базовый класс "Фигура" и его классы-потомки "Круг", "Прямоугольник" и "Треугольник". Классы-потомки наследуют свойства и методы базового класса "Фигура" и могут добавлять свои собственные свойства и методы. При создании класса-потомка мы можем использовать ключевое слово `extends`, чтобы указать, какой класс является его базовым. Основные преимущества наследования: 1. Повторное использование кода: благодаря наследованию мы можем использовать уже существующий код класса-родителя в классе-потомке, что позволяет избежать дублирования кода и упрощает разработку и поддержку программного кода. 2. Расширение функциональности: Класс-потомок может добавлять свои собственные свойства и методы, расширяя функциональность базового класса. Это позволяет создавать более специализированные классы и улучшать архитектуру Полиморфизм в C# позволяет объектам разных типов представлять себя в виде общего базового типа. Это означает, что объекты разных классов могут иметь одинаковую форму, то есть интерфейс или общую абстрактную базовую реализацию, и могут быть использованы вместе согласно этому общему интерфейсу или базовой реализации. В C# полиморфизм можно реализовать двумя способами: через наследование и через интерфейсы. 1. Полиморфизм на основе наследования: Полиморфизм на основе наследования достигается через создание иерархии классов, где базовый класс определяет общий интерфейс или поведение, а производные классы предоставляют свои собственные реализации для этого интерфейса или поведения. При таком 10 подходе можно создавать объекты производных классов и использовать их вместо объекта базового класса. На этапе компиляции тип переменной определяется как базовый класс, но на этапе выполнения будет использован метод или свойство, определенные в производном классе, если такой метод или свойство переопределен. 2. Полиморфизм на основе интерфейсов: Полиморфизм на основе интерфейсов позволяет создать общий контракт, который должен быть реализован классами. Интерфейс определяет набор методов и свойств, которые класс должен реализовать. Классы, реализующие этот интерфейс, могут быть использованы вместе, поскольку они предоставляют одинаковый набор методов и свойств по контракту интерфейса. Полиморфизм позволяет писать гибкий и расширяемый код, поскольку вы можете использовать разные объекты, реализующие общий интерфейс или базовый класс, без необходимости знать их конкретный тип. Это делает код легко изменяемым и расширяемым в будущем. Абстрактный класс и интерфейс могут содержать как абстрактные методы, так и обычные методы с реализацией. Класс, который наследуется от абстрактного класса, должен реализовать все его абстрактные методы, а класс, который реализует интерфейс, должен обеспечить реализацию всех методов интерфейса. Абстракция позволяет создавать гибкую и расширяемую архитектуру приложения, где классы могут быть заменены или расширены без изменения кода, который зависит от них. В соответствии с принципами ООП разработаем все необходимые нам объекты или их классы. 11 Описание разработанных классов internal class Client { private string surname; //фамилия Клиента private dinList pTemp, first, last, vspom; //первый элемент в списке private int count; public Client(string _surname) //конструктор public dinList First { get { return first; } } public string Surname { get { return surname; } } public void add(string public void dell(string adateOperation, int atransactionAmount) //добавление dateOperation, int //удаление public int sumOperation() //сумма public string getInfo() //получить информацию class dinList //класс для записи элементов в список { public Operation data; private dinList next; public dinList(Operation _data) //конструктор { data = _data; } public dinList Next//методы доступа к next 12 transactionAmount) internal class Operation { private string dateOperation; //Дата операции private int transactionAmount; //сумма операции public Operation(string adateOperation, int atransactionAmount)//конструктор public string getdate() { return dateOperation; } //получить дату операции public void setdate(string adateOperation) { dateOperation = adateOperation; } //поменять дату операции public int getTransaction() { return transactionAmount; } //получить сумму операции public void setTransaction(int atransactionAmount) transactionAmount = atransactionAmount; } //поменять сумму операции } 13 { Описание демонстрационного модуля с характеристикой использованных стандартных компонентов и списком реализованных обработчиков (Рис.5 Основной модуль) При выполнении проекта использовались такие компоненты как: Панель: для объединения нескольких других стандартных компонентов в группы по их функционалу для более удобной работы с приложением Надпись: самый распространенный компонент, который выглядит как обычная текстовая строка. Позволяет подписать определённые элементы формы, что сильно повышает удобство пользования приложением. В форме их объявлено большое множество, в этом можно убедится, просто запустив программу. Поле ввода: компонент, предназначенный для получения данных от пользователя и отправки их в соответствующие функции. 14 Кнопка: также один из важнейших компонентов, служит для вызова обработчиков событий, которые и являются всем функционалом приложения. Строковая таблица: таблица данных, в которой можно удобно разместить большое количество информации. 15 Реализованные обработчики событий 1. Создание лицевого счета с заданным клиента В данной части кода определен метод add(), который выполняет добавление новой операции в список. На вход метод принимает два параметра - adateOperation (тип string) и atransactionAmount (тип int), которые представляют дату операции и сумму транзакции соответственно. Сначала создается новый объект Operation с переданными значениями параметров adateOperation и atransactionAmount. Затем создается объект dinList с новой операцией newOperation в качестве параметра конструктора. Далее проверяется условие, если список пустой (first == null), то устанавливается first и last равными созданному объекту operationList. Также устанавливается указатель next для first равным null. Если список не пустой, то выполняется ветвление: - Если значение транзакции новой операции больше значения транзакции первой операции в списке (first.data.getTransaction()), то выполняются следующие действия: сохраняется текущее значение first в переменную vspom, затем first устанавливается равным operationList, next для first устанавливается равным vspom. Затем инкрементируется переменная count и метод завершает свою работу. - Если значение транзакции новой операции меньше значения транзакции последней операции в списке (last.data.getTransaction()), то выполняются следующие действия: сохраняется текущее значение last в переменную vspom, затем last устанавливается равным operationList, next для vspom устанавливается равным last. Затем инкрементируется переменная count и метод завершает свою работу. - Если новая операция должна быть вставлена в середину списка, то выполняются следующие действия: сохраняется текущее значение first в переменную vspom. Затем выполняется цикл while, пока vspom не станет null. 16 Внутри цикла проверяется условие, если значение транзакции текущей операции (vspom.data.getTransaction()) меньше значения транзакции новой операции, а значение (vspom.Next.data.getTransaction()) транзакции больше следующей значения операции транзакции новой операции. Если условие выполняется, то сохраняется текущее значение next для vspom в переменную pTemp, затем next для vspom устанавливается равным operationList, а next для operationList устанавливается равным pTemp. Затем инкрементируется переменная count и метод завершает свою работу. - Если условие внутри цикла не выполняется, то присваивается новое значение next для vspom (vspom = vspom.Next). Таким образом, метод add() выполняет добавление новой операции в список в соответствии с заданными условиями для его упорядочивания по возрастанию значения транзакции. public void add(string adateOperation, int atransactionAmount) //добавление { Operation newOperation = new Operation(adateOperation, atransactionAmount); dinList operationList = new dinList(newOperation); if (first == null) { first = operationList; last = operationList; first.Next = null; } else { if (first.data.getTransaction() > operationList.data.getTransaction()) { vspom = first; first = operationList; first.Next = vspom; count++; return; } else if (last.data.getTransaction() < operationList.data.getTransaction()) { vspom = last; last = operationList; vspom.Next = last; count++; return; } else { vspom = first; while (vspom != null) 17 { if (vspom.data.getTransaction() < operationList.data.getTransaction() && vspom.Next.data.getTransaction() > operationList.data.getTransaction()) { pTemp = vspom.Next; vspom.Next = operationList; vspom.Next.Next = pTemp; count++; return; } else { vspom = vspom.Next; } } } } 2. Сохранение объектной структуры во внешний файл Данный код относится к сохранению текстового файла с определенным содержимым на диске. Объявляется переменная path типа string, которая будет использоваться для хранения пути к файлу. Объявляется переменная fileText типа string и инициализируется значением "Файл". Используется конструкция using с классом SaveFileDialog для создания диалогового окна сохранения файла. В данном случае задается фильтр для расширений файлов - txt файлы или все файлы. Внутри блока using выполняется проверка, была ли нажата кнопка "ОК" в диалоговом окне сохранения файла. Если кнопка "ОК" была нажата, то переменной path присваивается путь к выбранному файлу, полученный через свойство FileName объекта saveFile. К переменной fileText добавляется строка с количеством элементов в коллекции clients, и последующее значение получается добавлением вызова метода infoLoad() у объекта clients. Используется конструкция using с классом FileStream для создания потока для записи данных в файл по пути, хранящемся в переменной path. Режим открытия или создания файла FileMode.OpenOrCreate. 18 задается через параметр Используется конструкция using с классом StreamWriter для записи данных в поток файла. Внутри блока using выполняется запись строки fileText в поток через метод Write объекта stream. Выполняется закрытие объектов stream и file, вызывая метод Close() у каждого из них. Закрытие потока файла важно для освобождения ресурсов после выполнения операций записи. Выполнение кода продолжается после блока using, если условие в блоке using SaveFileDialog не выполнилось или если сохранение файла прошло неуспешно. string path; string fileText = "Файл"; using (SaveFileDialog saveFile = new SaveFileDialog() { Filter = "txt files (*.txt)|*.txt|All files (*.*)|*.*" }) { if (saveFile.ShowDialog() == DialogResult.OK) { path = saveFile.FileName; fileText += "\n" + clients.Count + "\n"; fileText += clients.infoLoad(); using (FileStream file = new FileStream(path, FileMode.OpenOrCreate)) using (StreamWriter stream = new StreamWriter(file)) { stream.Write(fileText); stream.Close(); file.Close(); } } } 3. Загрузка объектной структуры из внешнего файла Данный код открывает диалоговое окно для выбора файла (с расширением .txt или любым другим файлом) с помощью OpenFileDialog. Если пользователь выбирает файл и нажимает "ОК", то в переменную path записывается путь к выбранному файлу. Далее, используя StreamReader, открывается выбранный файл для чтения. Считывается первая строка файла и записывается в переменную doIt. Если значение переменной doIt равно "Файл", то выполняется следующий код: 19 - В цикле for происходит очищение списка clients, очищение таблиц dataGridClient и dataGridTransaction. - Считывается следующая строка файла и преобразуется в число, которое записывается в переменную Count. - В цикле for происходит чтение фамилий клиентов из файла и добавление их в список clients, а также добавление этих фамилий в таблицу dataGridClient. При этом, если текущая итерация не является последней, то значение i увеличивается на 1, иначе происходит выход из цикла. - Считывается следующая строка файла и преобразуется в число, которое записывается в переменную LCount. - Если LCount не равно 0, то во внутреннем цикле for происходит чтение данных и суммы из файла, добавление этих данных и суммы в объект клиента, и добавление этих данных и суммы в таблицу dataGridTransaction. При этом, если текущая итерация не является последней, то значение i увеличивается на 1, иначе происходит выход из цикла. - После каждого внутреннего цикла hall увеличивается на 1. Если значение переменной doIt не равно "Файл", то выводится сообщение "Данный файл не подходит для работы с программой". Наконец, StreamReader закрывается. string path = "", doIt = ""; int Count, LCount, sum; using (OpenFileDialog openFile = new OpenFileDialog() { Filter = "txt files (*.txt)|*.txt|All files (*.*)|*.*" }) { if (openFile.ShowDialog() == DialogResult.OK) { path = openFile.FileName; } } using (StreamReader stream3 = new StreamReader(path)) { doIt = stream3.ReadLine(); if (doIt == "Файл") { for (int i = 0; i <= clients.Count; i++) { clients.delAllClient(); dataGridClient.Rows.Clear(); dataGridTransaction.Rows.Clear(); } Count = int.Parse(stream3.ReadLine()); for (int i = 1; i <= Count;) 20 { string surname = stream3.ReadLine(); clients.AddClient(surname); dataGridClient.Rows.Add(); dataGridClient.Rows[i - 1].Cells[0].Value = surname; if (i != Count) i++; else break; } stream3.ReadLine(); int hall = 0; while (stream3.Peek() != -1) { LCount = int.Parse(stream3.ReadLine()); if (LCount != 0) { for (int i = 1; i <= LCount;) { dataGridTransaction.Rows.Add(); string data = stream3.ReadLine(); sum = int.Parse(stream3.ReadLine()); string name = Convert.ToString(dataGridClient.Rows[hall].Cells[0].Value); clients.Find(name).add(data, sum); dataGridTransaction.Rows[i - 1].Cells[0].Value = data; dataGridTransaction.Rows[i - 1].Cells[1].Value = sum; if (i != LCount) i++; else break; } hall++; } } } else { MessageBox.Show("Данный файл не подходит для работы с программой"); } stream3.Close(); } 4. Удаление первого лицевого счета в очереди Данный код представляет собой метод "dell" (удаление), который принимает два аргумента: "dateOperation" (дата операции) типа string и "transactionAmount" (сумма транзакции) типа int. В начале метода создается переменная "vspom" и присваивается значение "first", которое предположительно является первым элементом связанного списка. Затем начинается цикл "while" с условием, что "vspom" не равен нулю. То есть цикл будет выполняться до тех пор, пока не достигнут конец списка. Внутри цикла сначала проверяется первый элемент списка. Если "vspom" равен "first" и значение даты операции ("vspom.data.getdate()") 21 совпадает с переданной датой ("dateOperation"), а также значение суммы транзакции ("vspom.data.getTransaction()") совпадает с переданной суммой ("transactionAmount"), то выполняются следующие действия: Обновляется значение "first" на "vspom.Next" (следующий элемент списка). Уменьшается значение переменной "count" (предположительно, это счетчик элементов списка) на единицу. Происходит выход из цикла "break;" Если первый элемент списка не удовлетворяет условию, то выполняется вторая часть "else if". Если значение даты операции и суммы транзакции в текущем элементе списка соответствуют переданным значениям, то происходят следующие действия: Устанавливается следующий элемент списка ("pTemp.Next") равным следующему элементу после текущего элемента ("vspom.Next"). Уменьшается значение переменной "count" на единицу. Происходит выход из цикла "break;" Если ни одно из условий не выполняется, то выполняется блок "else". В этом случае: Значение "pTemp" (временная переменная) присваивается значению "vspom" (текущий элемент списка). Значение "vspom" переходит к следующему элементу списка ("vspom = vspom.Next"). После окончания цикла метод завершается. public void dell(string dateOperation, int transactionAmount) //удаление { vspom = first; while (vspom != null) { if (vspom == first && vspom.data.getdate().Equals(dateOperation) && vspom.data.getTransaction() == transactionAmount) { first = vspom.Next; count--; break; } else if (vspom.data.getdate().Equals(dateOperation) && vspom.data.getTransaction() == transactionAmount) { 22 pTemp.Next = vspom.Next; count--; break; } else { pTemp = vspom; vspom = vspom.Next; } } } 5. Создание и добавление счетов в банковскую сеть 6. Вывод всех данных определенного лицевого счета 7. Создание и добавление клиентов в список-формуляр счетов 8. Удаление данных по фамилии клиента 9. Выход из приложения 23 Описание структуры проекта в соответствии с использованным инструментом разработки Весь проект был разработан в среде разработки программного обеспечения Visual Studio Сode. Это значит, что все программы написаны на языке C# и обрабатывались в свободно распространяемом компиляторе Visual Studio. (рис.6 Директория программы) (рис.7 Директория проекта) Папка bin является контейнерной папкой проекта, содержит выходные файлы, вызывается с помощью MSBuild. Папка obj имеет такую же функцию, как и папка bin. Их различие, в том, что папка bin- конечный результат компилирования, папка obj – временная. Папка properties содержит заданные свойства в проекте. 24 Файлы конфигурации, App.config содержит в себе данные конфига. Файлы СS содержит исходный код, написанный на объектноориентированном языке программирования C#. Файлы расширения resx сохраняет строки и объекты для приложений в формате XML. Файлы с расширением csproj хранят настройки проекта и ссылки на все ресурсы, используемые в проекте. Файл Cursovaya.sln файл запуска проекта и среды разработки. Файл Save, файл, который выполняет функцию носителя данных для сохранения и выгрузки 25 Список литературы 1. Зыков, С. В. Введение в теорию программирования. Объектноориентированный подход : учебное пособие / С. В. Зыков. — 3-е изд. — Москва : Интернет-Университет Информационных Технологий (ИНТУИТ), Ай Пи Ар Медиа, 2021. — 187 c. 2. Маляров, А. Н. Объектно-ориентированное программирование : учебник для СПО / А. Н. Маляров. — 2-е изд. — Саратов : Профобразование, 2023. — 334 c Объектно-ориентированное 3. методическое пособие/ программирование. Учебно- Козин А.Н., Якунина Е.А. – Казань: УВО «Университет управления «ТИСБИ», 2020. 4. Логанов, С. В. Объектно-ориентированное программирование : учебное пособие для СПО / С. В. Логанов, С. Л. Моругин. — Саратов, Москва : Профобразование, Ай Пи Ар Медиа, 2022. — 215 c. 5. Городняя, Л. В. Основы функционального программирования : учебное пособие / Л. В. Городняя. — 3-е изд. — Москва : ИнтернетУниверситет Информационных Технологий (ИНТУИТ), Ай Пи Ар Медиа, 2021. — 246 c. 6. Биллиг, В. А. Основы программирования на C# : учебное пособие / В. А. Биллиг. — 3-е изд. — Москва : Интернет-Университет Информационных Технологий (ИНТУИТ), Ай Пи Ар Медиа, 2021. — 573 c. 7. Биллиг, В. А. Основы объектного программирования на C# (С# 3.0, Visual Studio 2008) : учебник / В. А. Биллиг. — 3-е изд. — Москва : ИнтернетУниверситет Информационных Технологий (ИНТУИТ), Ай Пи Ар Медиа, 2021. — 409 c. 8. Немцова, Т. И. Программирование на языке высокого уровня. Программирование на языке Object Pascal : учебное пособие / Т. И. Немцова, С. Ю. Голова, И. В. Абрамова ; под ред. Л. Г. Гагариной. — Москва : ФОРУМ : ИНФРА-М, 2022. 26 9. Гребенникова, Н. И. Программирование на языке высокого уровня : лабораторный практикум / Н. И. Гребенникова, М. Ю. Сергеев, Т. И. Сергеева. — Воронеж : Воронежский государственный технический университет, ЭБС АСВ, 2021. — 94 c. 10. Якимов, С. П. Структурное программирование : учебное пособие для вузов / С. П. Якимов. — Москва : Издательство Юрайт, 2022. — 342 с. 11. Структуры и алгоритмы обработки данных. Часть 2. Усложненные структуры данных. Теория и методика обучения. Учебно-методическое пособие/ А.Н. Козин, Л.Б. Таренко – Казань: УВО «Университет управления «ТИСБИ», 2020. 27 Листинг кода using using using using using System; System.Collections.Generic; System.Linq; System.Text; System.Threading.Tasks; namespace Cursovaya { internal class AllClient { private Client[] queue; int count; public AllClient() { queue = new Client[10]; count = 0; } public int Count { get { return count; } } public void AddClient(string suranmeClient) {с Client newClient = new Client(suranmeClient); if (count > queue.Length) { Array.Resize(ref queue, queue.Length * 2); } queue[count] = newClient; count++; } public void DeleteClient() { for (int i = 0; i < count; i++) { queue[i] = queue[i + 1]; } queue[count] = null; count--; } public Client Find(string surname) { for (int i = 0; i < count; i++) { if (queue[i].Surname.Equals(surname)) { return queue[i]; } } return null; } public string GetAllClientInfo() { string allInfo = ""; for (int i = 0; i < count; i++) { allInfo += queue[i].getInfo().ToString(); } allInfo += "\nSum all client transaction: " + SumPriceAllClients(); return allInfo; } public int SumPriceAllClients() 28 { int allSum = 0; for (int i = 0; i < count; i++) { allSum += queue[i].sumOperation(); } return allSum; } public string infoLoad() { string data = ""; for (int i = 0; i < count; i++) { data += queue[i].Surname + "\n"; } data += "\n"; for (int i = 0; i < count; i++) { data += queue[i].getInfoLoad(); } return data; } public void delAllClient() { for (int i = 0; i < count; i++) { queue[i] = null; } } } } using using using using using System; System.Collections.Generic; System.Linq; System.Text; System.Threading.Tasks; namespace Cursovaya { internal class Client { private string surname; //фамилия Клиента private dinList pTemp, first, last, vspom; //первый элемент в списке private int count; public Client(string _surname) //конструктор { this.surname = _surname; first = null; count = 0; } public dinList First { get { return first; } } public string Surname { get { return surname; } } public void add(string adateOperation, int atransactionAmount) //добавление { Operation newOperation = new Operation(adateOperation, atransactionAmount); dinList operationList = new dinList(newOperation); if (first == null) { first = operationList; last = operationList; first.Next = null; } else 29 { if (first.data.getTransaction() > operationList.data.getTransaction()) { vspom = first; first = operationList; first.Next = vspom; count++; return; } else if (last.data.getTransaction() < operationList.data.getTransaction()) { vspom = last; last = operationList; vspom.Next = last; count++; return; } else { vspom = first; while (vspom != null) { if (vspom.data.getTransaction() < operationList.data.getTransaction() && vspom.Next.data.getTransaction() > operationList.data.getTransaction()) { pTemp = vspom.Next; vspom.Next = operationList; vspom.Next.Next = pTemp; count++; return; } else { vspom = vspom.Next; } } } } } public void dell(string dateOperation, int transactionAmount) //удаление { vspom = first; while (vspom != null) { if (vspom == first && vspom.data.getdate().Equals(dateOperation) && vspom.data.getTransaction() == transactionAmount) { first = vspom.Next; count--; break; } else if (vspom.data.getdate().Equals(dateOperation) && vspom.data.getTransaction() == transactionAmount) { pTemp.Next = vspom.Next; count--; break; } else { pTemp = vspom; vspom = vspom.Next; } 30 } } public int sumOperation() //сумма { int sum = 0; vspom = first; while (vspom != null) { sum += vspom.data.getTransaction(); vspom = vspom.Next; } return sum; } public string getInfo() //получить информацию { string info = "\n\nSurname-" + surname + "."; vspom = first; while (vspom != null) { info += "\nDate: " + vspom.data.getdate(); info += " transaction: " + vspom.data.getTransaction(); vspom = vspom.Next; } info += "\nSum transaction: " + sumOperation(); return info; } public string getInfoLoad() { string info = ""; info += count + 1; vspom = first; while (vspom != null) { info += "\n" + vspom.data.getdate(); info += "\n" + vspom.data.getTransaction(); vspom = vspom.Next; } return info; } } class dinList //класс для записи элементов в список { public Operation data; private dinList next; public dinList(Operation _data) //конструктор { data = _data; } public dinList Next//методы доступа к next { get { return next; } set { next = value; } } } } using using using using using System; System.Collections.Generic; System.Linq; System.Text; System.Threading.Tasks; namespace Cursovaya { internal class Operation { 31 private string dateOperation; //Дата операции private int transactionAmount; //сумма операции public Operation(string adateOperation, int atransactionAmount)//конструктор { dateOperation = adateOperation; transactionAmount = atransactionAmount; } public string getdate() { return dateOperation; } //получить дату операции public void setdate(string adateOperation) { dateOperation = adateOperation; } //поменять дату операции public int getTransaction() { return transactionAmount; } //получить сумму операции public void setTransaction(int atransactionAmount) { transactionAmount = atransactionAmount; } //поменять сумму операции } } using using using using using using using using using using using System; System.Collections.Generic; System.ComponentModel; System.Data; System.Drawing; System.IO; System.Linq; System.Text; System.Text.RegularExpressions; System.Threading.Tasks; System.Windows.Forms; namespace Cursovaya { public partial class Form1 : Form { AllClient clients = new AllClient(); public Form1() { InitializeComponent(); } private void dataGridClient_CellClick(object sender, DataGridViewCellEventArgs e) { dataGridTransaction.Rows.Clear(); string text = dataGridClient.CurrentCell.Value.ToString(); Client vspom = clients.Find(text); dinList help = vspom.First; while (help != null) { dataGridTransaction.Rows.Add(help.data.getdate(), help.data.getTransaction()); help = help.Next; } } private void Form1_Load(object sender, EventArgs e) { } private void dataGridClient_CellContentClick(object sender, DataGridViewCellEventArgs e) { } 32 private void dataGridTransaction_CellContentClick(object sender, DataGridViewCellEventArgs e) { } private void textData_TextChanged(object sender, EventArgs e) { } private void butaddClient_Click_1(object sender, EventArgs e) { clients.AddClient(textBoxSurname.Text); dataGridClient.Rows.Add(textBoxSurname.Text); } private void butDelClient_Click_1(object sender, EventArgs e) { clients.DeleteClient(); dataGridClient.Rows.Clear(); dataGridTransaction.Rows.Clear(); } private void butAddtransaction_Click_1(object sender, EventArgs e) { clients.Find(dataGridClient.CurrentCell.Value.ToString()).add(textData.Text, Convert.ToInt32(textCost.Text)); dataGridTransaction.Rows.Clear(); dinList vspom = clients.Find(dataGridClient.CurrentCell.Value.ToString()).First; while (vspom != null) { dataGridTransaction.Rows.Add(vspom.data.getdate(), vspom.data.getTransaction()); vspom = vspom.Next; } } private void butDellTransaction_Click_1(object sender, EventArgs e) { clients.Find(dataGridClient.CurrentCell.Value.ToString()). dell(dataGridTransaction.CurrentRow.Cells[0].Value.ToString(), Convert.ToInt32(dataGridTransaction.CurrentRow.Cells[1].Value.ToString())); dataGridTransaction.Rows.Clear(); dinList vspom = clients.Find(dataGridClient.CurrentCell.Value.ToString()).First; while (vspom != null) { dataGridTransaction.Rows.Add(vspom.data.getdate(), vspom.data.getTransaction()); vspom = vspom.Next; } } private void butSave_Click_1(object sender, EventArgs e) { string path; string fileText = "Файл"; using (SaveFileDialog saveFile = new SaveFileDialog() { Filter = "txt files (*.txt)|*.txt|All files (*.*)|*.*" }) { if (saveFile.ShowDialog() == DialogResult.OK) { path = saveFile.FileName; 33 fileText += "\n" + clients.Count + "\n"; fileText += clients.infoLoad(); using (FileStream file = new FileStream(path, FileMode.OpenOrCreate)) using (StreamWriter stream = new StreamWriter(file)) { stream.Write(fileText); stream.Close(); file.Close(); } } } } private void butLoad_Click_1(object sender, EventArgs e) { string path = "", doIt = ""; int Count, LCount, sum; using (OpenFileDialog openFile = new OpenFileDialog() { Filter = "txt files (*.txt)|*.txt|All files (*.*)|*.*" }) { if (openFile.ShowDialog() == DialogResult.OK) { path = openFile.FileName; } } using (StreamReader stream3 = new StreamReader(path)) { doIt = stream3.ReadLine(); if (doIt == "Файл") { for (int i = 0; i <= clients.Count; i++) { clients.delAllClient(); dataGridClient.Rows.Clear(); dataGridTransaction.Rows.Clear(); } Count = int.Parse(stream3.ReadLine()); for (int i = 1; i <= Count;) { string surname = stream3.ReadLine(); clients.AddClient(surname); dataGridClient.Rows.Add(); dataGridClient.Rows[i - 1].Cells[0].Value = surname; if (i != Count) i++; else break; } stream3.ReadLine(); int hall = 0; while (stream3.Peek() != -1) { LCount = int.Parse(stream3.ReadLine()); if (LCount != 0) { for (int i = 1; i <= LCount;) { dataGridTransaction.Rows.Add(); string data = stream3.ReadLine(); sum = int.Parse(stream3.ReadLine()); string name = Convert.ToString(dataGridClient.Rows[hall].Cells[0].Value); clients.Find(name).add(data, sum); dataGridTransaction.Rows[i - 1].Cells[0].Value = data; 34 dataGridTransaction.Rows[i - 1].Cells[1].Value = sum; if (i != LCount) i++; else break; } hall++; } } } else { MessageBox.Show("Данный файл не подходит для работы с программой"); } stream3.Close(); } } private void textBoxSurname_TextChanged(object sender, EventArgs e) { } } } 35