ТРКП курсовая. Мельник Е. В. Группа в353090400321. Фитнестрекер
Скачать 369.24 Kb.
|
Санкт-Петербургский Политехнический Университет Институт компьютерных наук и технологий «Высшая школа программной инженерии» Курсовое проектирование по дисциплине: «Технологии разработки качественного программного обеспечения» по теме: «Фитнес-трекер» Выполнил: студент гр. в3530904/00321 Мельник Е. В. Руководитель: Смирнов Н. Г. Санкт-Петербург 2023 СОДЕРЖАНИЕ1 ВВЕДЕНИЕ 3 1.1 Цель работы 3 1.2 Постановка задачи 3 2 Архитектура приложения 4 2.1 Диаграмма классов 4 2.2 Диаграмма компонентов 7 2.3 Диаграмма последовательностей 7 3 Работа программы 8 4 Тестирование 12 5 Вывод 14 ПРИЛОЖЕНИЕ 15 Main 15 WorkoutSession 15 Profile 20 Training 21 TestWorkout 21 1 ВВЕДЕНИЕ1.1 Цель работыЦелью данной курсовой работы является создание программного продукта «Фитнес-трекер», удовлетворяющего следующему описанию: Приложение позволяет указать вид тренировки: отжимание, скакалка, приседания. Доступны такие команды, как: начать тренировку (запомнить время начала) и закончить тренировку (запомнить время окончания). За отработанное время высчитывается количество потраченных калорий по формуле: K - количество калорий в час, затрачиваемое на определенный вид тренировки, умножается на t – продолжительность тренировки в часах. От запуска к запуску программы данные должны сохраняться и общее количество калорий - суммироваться. Для этого необходимо создать файл с обращением к нему при помощи операций сериализации и десериализации. 1.2 Постановка задачиПриложение должно удовлетворять следующим требованиям: Программа считывает файл с серилизованными данными профилей пользователей; Пользователь может создавать новые профили и выбирать уже существующие; Пользователь выбирает номер интересующей его тренировки при помощи клавиатуры; Спустя некоторое время пользователь обязан ввести «stop» для завершения тренировки и фиксации времени её окончания; После завершения тренировки данные о сожженных калориях и затраченном времени записываются в профиль; При вводе завершении тренировок программа завершает свою работу и сохраняет данные в файл; 2 Архитектура приложения2.1 Диаграмма классовUML-диаграмма классов данного приложения состоит из четырёх компонентов, которые можно рассмотреть на рисунке 1. Класс «Main» содержит в себе только один метод «main()», который создаёт экземпляр класса «Workout Session» и вызывает у него метод «run» Класс «WorkoutSession» содержит методы для отслеживания и записи тренировок пользователя. В классе определены следующие методы: isInteger() - проверяет, является ли объект типа Integer. Если это так, метод возвращает true. Если объект не является типом Integer, метод преобразует его в строку и пытается преобразовать в целое число. Если это возможно, метод также возвращает true, в противном случае метод возвращает false. readUsers() - читает список пользователей из файла users.out, если он существует. Если файл не существует, метод ничего не делает. Десериализация объектов производится с помощью класса ObjectInputStream. login() - спрашивает у пользователя, есть ли у него профиль. Если ответ "y", список доступных профилей отображается на экране, и пользователь должен выбрать один. Если ответ "n", пользователю будет предложено написать свое имя и создать новый профиль. process() - позволяет пользователю выбрать тренировку из списка, начать ее и записать результаты. Сначала пользователю будет предложено выбрать тренировку из списка доступных тренировок. Затем пользователю будет предложено начать тренировку. Если пользователь начал тренировку, ему будет предложено записать время, которое он потратил на тренировку, и количество калорий, которое он сжег во время тренировки. Затем пользователю будет предложено продолжить тренировку или закончить. Если пользователь решил продолжить, процесс начнется заново. Если пользователь решил закончить, его результаты будут добавлены в его профиль. run() - выполняет следующие действия: Создает ArrayList объектов Training с помощью метода createWorkout(). Инициализирует новый ArrayList объектов Profile в переменной users. Считывает существующих пользователей с помощью метода readUsers(). Просит пользователя ввести свои учетные данные для входа в систему с помощью метода login(). Обрабатывает тренировку с помощью метода process(), используя объект workout. Записывает обновленную информацию о пользователях в файл users.out с помощью FileOutputStream и ObjectOutputStream. Если при выполнении метода возникнет ошибка ввода-вывода (IOException), некорректное имя (InvalidNameException) или проблема с десериализацией объекта (ClassNotFoundException), метод сгенерирует соответствующее исключение и вызовет их вверх по стеку вызовов. Также класс содержит два поля: userId (идентификатор пользователя) и users (список профилей пользователей). Класс «Training» содержит методы для отслеживания и записи тренировок пользователя также содержит три поля: type, calPerHour и allCal. Конструктор класса принимает два аргумента: type (тип тренировки) и calPerHour (количество калорий, которое сжигается за час при выполнении данной тренировки). Значения этих аргументов используются для инициализации соответствующих полей класса. В классе определены следующие методы: getCalPerHour() - возвращает количество калорий, которое сжигается за час при выполнении данной тренировки. display() - выводит тип тренировки на экран. getType() - возвращает тип тренировки. clear() - устанавливает значение поля allCal в ноль. Класс «Profile» представляет профиль пользователя, который может отслеживать время и количество потраченных калорий. Он имплементирует интерфейс Serializable, что означает, что объекты этого класса могут быть сериализованы и десериализованы, для сохранения в файле. Класс содержит три поля: name - строка, которая хранит имя пользователя; allCal - число типа double, которое хранит общее количество потраченных калорий; allTime - число типа double, которое хранит общее время тренировок пользователя. Класс имеет несколько методов для управления этими полями: addTime() - добавляет время тренировки в общее время пользователя; addCal() - добавляет количество потраченных калорий в общее количество потраченных калорий пользователя; getName() - возвращает имя пользователя; getTime() - возвращает общее время тренировок пользователя; getCal() - возвращает общее количество потраченных калорий пользователя. Рисунок 1 – UML-диаграмма классов 2.2 Диаграмма компонентовДанная диаграмма состоит из двух компонентов: WorkoutSession, где происходит функционирование всей логики программы, users.out – содержащий информацию по всем профилям. После запуска приложения, WorkoutSession считывает информацию из файла и хранит её. Запись новых данных и обновление информации о уже существующих профилях, происходит по завершению программы. Вышеописанная схема представлена на рисунке 2. Рисунок 2 – UML-диаграмма компонентов 2.3 Диаграмма последовательностейНа диаграмме последовательности показан жизненный цикл программы. В начале происходит запуск приложения, создание и старт тренировочной сессии. Далее пользователь должен либо создать новый профиль, либо выбрать уже существующий. Далее пользователю предоставляется выбор упражнения. Если ввод совершён правильно, то начинается тренировка. После ввода сообщение об окончании тренировки происходит вывод информации о ней на экран. Затем, цикл работы программы может быть повторён. Если пользователь пожелает корректно завершить работу приложения, тогда следует отказаться от продолжения тренировки при соответствующем запросе, после чего программа будет завершена и его профиль, с результатами тренировки, будет сохранён в файл. Временная диаграмма продемонстрирована на рисунке 3. Рисунок 3 – UML-диаграмма последовательностей 3 Работа программыПри запуске приложения пользователю будет предложено создать нового пользователя или выбрать уже существующего. Если при считывании файла с пользователями, не было обнаружено ранее созданных пользователей, будет существующее сообщение, показанное на показанное на рисунке 4. Рисунок 4 – Сообщение если в файле не обнаружено пользователей При создании нового профиля, пользователь просто вводит его имя и в дальнейшем его можно выбирать его из списка доступных профилей. Процесс создания и выбора нового профиля показан на рисунке 5. Рисунок 5 – Создание и выбор нового профиля Далее пользователю выводится список доступных упражнений, которые выбираются вводом из порядкового номера, после выбора упражнения, последует запрос на подтверждение выбора и при положительном ответе, тренировка начнётся. Процесс выбора и начала тренировки показан на рисунке 6. Рисунок 6 – Процесс выбора и начала тренировки Для завершения выполнения упражнения, пользователь должен ввести “stop”, после чего будут выведено затраченное время и сожжённые в результате упражнения калории. Результат выполнения приседаний показан на рисунке 7. Рисунок 7 – Результат выполнения приседаний в течении пары минут Далее пользователь может завершить тренировку или выбрать новое упражнение. На рисунке 8 показано продолжение тренировки и выбор прыжков через скакалку в качестве нового упражнения. Рисунок 8 – переход к прыжкам через скакалку Если по завершению упражнения отказаться от продолжения тренировки, итоговый результат будет выведен пользователю и записан в его профиль. На рисунке 9 показано как это выглядит. Рисунок 9 – Завершение тренировки При повторном запуске программы, ранее созданный профиль и данные о тренировках в нём, будут доступны пользователю. На рисунке 10 показано как это выглядит. Рисунок 10 – Выбор ранее созданного профиля 4 ТестированиеДля тестирования приложения был создан тест класс содержащий следующие методы: testFunc(): Внутри метода сначала создается экземпляр класса WorkoutSession, затем происходят проверки: Вызывается метод isInteger с аргументом "q", и ожидается, что метод вернет false. То есть тестируется, что метод корректно определяет, что "q" не является целым числом. Вызывается метод createWorkout, который создает список объектов типа Training. Затем происходит проверка, что каждый объект в списке имеет поле calPerHour, значение которого больше или равно нулю. То есть тестируется, что метод createWorkout корректно создает список объектов Training с правильными значениями свойств. testAfterWork(): Получается текущий путь к файлу и создается строка с именем файла "users.out". Затем создается объект File для этого файла и проверяется, существует ли файл. Если файл не существует, то тест не пройден. Создается новый пустой список ArrayList объектов типа Profile. Затем открывается файл "users.out" для чтения данных в бинарном формате. В цикле while читаются объекты из файла, которые должны быть списками Profile, и добавляются в список users. Цикл продолжается до тех пор, пока не достигнут конец файла или пока не возникнет исключение. После чтения данных из файла происходит проверка, что размер списка users не равен 0. Если список пустой, то тест не пройден. Данный текст проверяет, что после завершения работы программы в файл "users.out" были записаны корректные данные о профилях пользователей и файл существует. На рисунке 11 продемонстрированы результаты прошедшего тестирования. Рисунок 11 – Результат тестирования 5 ВыводВ результате выполнения курсового проектирования были получены навыки по построению диаграммы классов, диаграмм компонентов и диаграмм последовательностей, а также опыт работы по документированию программного продукта. Во время разработки проекта «Фитнес-трекер» использовалась библиотека java.io и java.util. java.io необходим для хранения бинарных данных в сериализированном виде, а также чтения их и вывод на экран пользователя. java.util используется для подключения списков и ввода с клавиатуры. Итоговое приложение соответствует заявленным требованиям. Наиболее важный модуль программы протестирован и исправно функционирует. ПРИЛОЖЕНИЕMainpackage Violet.WorkoutTracker; import javax.naming.InvalidNameException; import java.io.IOException; public class Main { public static void main(String[] args) throws IOException, InvalidNameException, ClassNotFoundException { WorkoutSession workout = new WorkoutSession(); workout.run(); } } WorkoutSessionpackage Violet.WorkoutTracker; import javax.naming.InvalidNameException; import java.io.*; import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; public class WorkoutSession { private boolean init = false; private int userId = 0; //private ArrayList private ArrayList users; public WorkoutSession(){ } public boolean isInteger(Object object) { if(object instanceof Integer) { return true; } else { String string = object.toString(); try { Integer.parseInt(string); } catch(Exception e) { return false; } } return true; } public void readUsers() throws IOException, ClassNotFoundException { Path currentPath = Paths.get("").toAbsolutePath(); String tmp = currentPath.toString() + "/users.out"; File f = new File(tmp); if (!f.exists()) return; FileInputStream fis = new FileInputStream("users.out"); boolean cont = true; try ( ObjectInputStream input = new ObjectInputStream(fis)){ while(cont) { ArrayList obj = (ArrayList ) input.readObject(); if (obj != null) { users = obj; } } } catch (IOException | ClassNotFoundException e) { cont = false; } } public void login() throws IOException, ClassNotFoundException { BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in)); String cmdOp; while (!init) { System.out.print("Do you have a profile?(y/n): "); cmdOp = bufferedReader.readLine(); if (cmdOp.equals("y")) { if (users.size() == 0) { System.out.println("profiles do not exist"); continue; } System.out.println("Available profiles: "); for (int i = 0; i < users.size(); i++) { System.out.print(i + 1 + " " + users.get(i).getName() + "\n"); } System.out.print("Select your profile:"); cmdOp = bufferedReader.readLine(); if (!isInteger(cmdOp)) { System.out.println("Try again"); continue; } userId = Integer.parseInt(cmdOp) - 1; if (userId >= users.size() || userId < 0) { System.out.println("Wrong number, try again"); continue; } init = true; break; } else if (cmdOp.equals("n")) { System.out.print("Write your name:"); cmdOp = bufferedReader.readLine(); if (cmdOp.equals("")){ System.out.println("Wrong name"); continue; } users.add(new Profile(cmdOp)); continue; } else continue; } } private void process(ArrayList BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in)); String cmdOp; while (init) { System.out.println(); for (int i = 0; i < workout.size(); i++) { System.out.print(i + 1 + " "); workout.get(i).display(); } System.out.print("Choose your workout: "); cmdOp = bufferedReader.readLine(); if (!isInteger(cmdOp)) { System.out.println("Write number and try again"); continue; } int exerciseId = Integer.parseInt(cmdOp); if (exerciseId > workout.size() || exerciseId < 1) { System.out.println("Wrong number, try again"); continue; } System.out.print("Start training?(y/n): "); cmdOp = bufferedReader.readLine(); double start = 0, time = 0; if (cmdOp.equals("y")) { start = System.currentTimeMillis(); System.out.print("Write `stop` when you finish: "); while (true) { if (cmdOp.equals("stop")) { time = (System.currentTimeMillis() - start) / (1e3 * 3600 * 1000); workout.get(exerciseId - 1).allCal += time * workout.get(exerciseId - 1).getCalPerHour(); break; } cmdOp = bufferedReader.readLine(); if (!cmdOp.equals("stop")){ System.out.print("Please, write word correctly(`stop`):"); } } } else continue; System.out.print("Time = " + time + " hour " + "\nyou spent " + workout.get(exerciseId - 1).allCal + " cal\n" + "your " + workout.get(exerciseId - 1).getType() + " was amazing, continue?(y/n): "); users.get(userId).addCal( workout.get(exerciseId - 1).allCal); users.get(userId).addTime( time ); workout.get(exerciseId - 1).clear(); cmdOp = bufferedReader.readLine(); if (cmdOp.equals("y")) { continue; } else if (cmdOp.equals("n")) { System.out.println("\n\nWell done " + users.get(userId).getName() + ", whole time = " + users.get(userId).getTime() + " hour " + "\nyou spent " + users.get(userId).getCal() + " cal"); break; } else System.out.println("Wrong, break"); break; } } public ArrayList ArrayList exercise.add(new Training("push_ups", 345.0)); exercise.add(new Training("jump rope", 413.0)); exercise.add(new Training("squats", 284.1)); return exercise; } public void run() throws IOException, InvalidNameException, ClassNotFoundException { ArrayList users = new ArrayList (); readUsers(); login(); process(workout); FileOutputStream fos = new FileOutputStream( "users.out" ); ObjectOutputStream out = new ObjectOutputStream(fos); out.writeObject(users); out.close(); } } Profilepackage Violet.WorkoutTracker; import java.io.Serializable; public class Profile implements Serializable { private String name; private double allCal = 0; private double allTime = 0; Profile(String name){ this.name = name; } public void addTime(double dt){ this.allTime += dt; } public void addCal(double new_cal){ this.allCal += new_cal; } public String getName(){ return this.name; } public double getTime(){ return this.allTime; } public double getCal(){ return this.allCal; } } Trainingpackage Violet.WorkoutTracker; public class Training{ private String type; private double calPerHour; public double allCal; Training(String type, double calPerHour){ this.type = type; this.calPerHour = calPerHour; } public double getCalPerHour(){ return calPerHour; } void display(){ System.out.println(type); } String getType(){ return type; } void clear(){ allCal = 0.0; } } TestWorkoutpackage Violet.WorkoutTracker; import org.junit.Assert; import org.junit.Test; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; public class TestWorkout { @Test public void testFunc() throws Exception{ WorkoutSession app = new WorkoutSession(); // проверка функций String word = "q"; Assert.assertFalse(app.isInteger(word)); ArrayList for (int i = 0; i < wrkt.size(); i++) { assertTrue( wrkt.get(i).getCalPerHour() >= 0); } } @Test public void testAfterWork() throws Exception{ //сначала запускается программа, потом уже проверяется //после окончания функции обязаельно: // -должен существовать файл для сериализации // -должен быть как минимум 1 профиль, после правильного завершения прог. Path currentPath = Paths.get("").toAbsolutePath(); String tmp = currentPath.toString() + "/users.out"; File f = new File(tmp); Assert.assertEquals(true, f.exists()); ArrayList users = new ArrayList<>(); FileInputStream fis = new FileInputStream("users.out"); boolean cont = true; try ( ObjectInputStream input = new ObjectInputStream(fis)){ while(cont) { ArrayList obj = (ArrayList ) input.readObject(); if (obj != null) { users = obj; } } } catch (IOException | ClassNotFoundException e) { cont = false; } Assert.assertNotEquals(0, users.size()); } } |