등급 계산기 (이전 버전의 새 OOP 버전)
저는 OOP 사고 방식을 개발하려고합니다. 나는 누군가가 내 성적 계산기 OOP 프로그램을 검토 할 귀중한 시간을 할애 할 수있을만큼 친절하기를 바랐습니다. 항상 그렇듯이 내가 잘한 일, 개선해야 할 점, 내가 가진 것을 개선 할 수있는 방법에 대한 제안을 알고 싶습니다. 그건 그렇고, Class라는 클래스가 있습니다. 혼동하지 않도록 "cls"로 시작해야합니다. 이 프로그램을 입력해야하는대로 취급하고 오류를 확인하지 않았습니다. 이 프로그램의 요점은 OOP에서 개발하는 것입니다.
// Task 1.cpp : This file contains the 'main' function. Program execution begins and ends there.
#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>
#include <numeric>
#include <string>
class TestPaper
{
public:
int m_scoreOutOf;
bool checkBoundary(int value, int boundary) {
if (value < 0 || value > boundary) {
std::cout << "Score must be between " << " 0 and " << boundary << ". Please try again.\n";
return false;
}
return true;
}
};
class Student {
private:
std::string m_name;
int m_scoreGot;
public:
Student(std::string name, int scoreGot)
:m_name(name), m_scoreGot(scoreGot){}
std::string getName() const { return m_name; }
int getScoreGot() const { return m_scoreGot; }
};
class Class {
private:
std::vector<Student>students;
public:
void AddStudent(TestPaper& testPaper) {
std::string name = "";
int scoreGot = 0;
std::cout << "Enter student name: ";
std::getline(std::cin >> std::ws, name);
do
{
std::cout << "\nWhat did " << name << " score?\nEnter a score between 0 and "
<< testPaper.m_scoreOutOf << ": ";
std::cin >> scoreGot;
} while (testPaper.checkBoundary(scoreGot, testPaper.m_scoreOutOf) == false);
students.push_back({ name, scoreGot });
}
std::vector<Student>& accessStudents() { return students; }
};
class GradeCalculator {
TestPaper m_testPaper;
Class m_ClassOfStudents;
public:
GradeCalculator(TestPaper testPaper, Class classOfStudents) :m_testPaper(testPaper), m_ClassOfStudents(classOfStudents) {}
void DisplayMenu() {
std::cout << "\n1. Add student and their grade\n";
std::cout << "2. Calculate class score\n";
std::cout << "3. Modify testpaper (haven't implemented this yet)\n";
}
double averageGrade() {
auto sum = std::transform_reduce(m_ClassOfStudents.accessStudents().begin(), m_ClassOfStudents.accessStudents().end(), 0.0, std::plus<>(),
[&](auto& student) { return calculateGradePercentage(student); });
return sum / m_ClassOfStudents.accessStudents().size();
}
double calculateGradePercentage(Student &student)
{
return static_cast<double>(student.getScoreGot()) / static_cast<double>(m_testPaper.m_scoreOutOf) * 100;
}
void DisplayResult() {
for (auto& student : m_ClassOfStudents.accessStudents()) {
std::cout << "Percentage scores are: \n";
std::cout << student.getName() << ": " << calculateGradePercentage(student) << "%\n";
}
std::cout << "Average grade perecentage: " << averageGrade() << "%\n";
}
void runProgram() {
int menuChoice = 0;
while (true)
{
DisplayMenu();
std::cout << "\nEnter a choice from the menu: ";
std::cin >> menuChoice;
switch (menuChoice)
{
case 1:
m_ClassOfStudents.AddStudent(m_testPaper);
break;
case 2:
DisplayResult();
break;
default:
std::cout << "Invalid choice!\n";
}
}
}
};
int main()
{
TestPaper testPaper({ 20 });
Class classOfStudents;
GradeCalculator calculator(testPaper, classOfStudents);
calculator.runProgram();
}
답변
다음은 프로그램 개선에 도움이되는 몇 가지 사항입니다.
I / O를 데이터 작업과 혼합하지 마십시오.
일반적으로를 가진 말 std::getline
과 std::cout
같은 데이터 클래스 내에서 Class
좋은 생각이 아니다. 클래스를 재사용하기가 더 어렵습니다. 더 나은 연습 (데이터를 유지하는 것입니다 Student
, Class
등)을 사용자로부터 입력을 받고에서 분리합니다. 모델 - 뷰 - 컨트롤러 디자인 패턴은이 같은 프로그램에 종종 유용하다. 이 답변ConsoleMenu
에서 와 같이 클래스와 같은 것을 사용하는 것을 고려하십시오 .
실용적인 경우 이동 의미 체계 사용
에서 AddStudent
기능, 우리는이 라인을 가지고 :
students.push_back({ name, scoreGot });
emplace_back컴파일러에게 생성하고 복사 할 필요는 없지만 객체를 제자리에 생성하는 것이 안전하다는 것을 알리는 것을 사용 하는 것이 더 좋습니다.
클래스 인터페이스 재고
Class
클래스는이 멤버 함수를 가지고 :
std::vector<Student>& accessStudents() { return students; }
이것은 나쁜 생각입니다. 내부 클래스 멤버에 대한 참조를 반환합니다. Class
인스턴스가 삭제되었지만 일부 외부 엔티티가 더 이상 존재하지 않는 데이터에 대한 참조를 여전히 보유하고있는 경우 어떻게되는지 생각해보십시오 . 사용되는 유일한 장소는 내부 GradeCalculator::averageGrade()
에 GradeCalculator::DislayResult()
있으므로 클래스 인터페이스에서 무언가 잘못되었다는 강력한 지표입니다. averageGrade()
함수를 Class
멤버 함수로 만드는 것이 좋습니다 .
합리적인 경우 기본값 사용
Student
생성자 본질적 컴파일러에 의해 생성 된 것이다 동일하다. 오류 가능성을 줄이기 위해 간단히 제거 할 수 있습니다.
사용자에 대해 생각
프로그램을 정상적으로 종료하는 확실한 방법은 없습니다. 메뉴 항목을 추가하는 것이 좋습니다. 또한 실제로 프로그램을 사용하는 사람은 학생 전체를 입력하고 전체 수업의 점수를 계산하기를 원할 가능성이 있습니다. "1. 학생 추가 ..."를 반복해서 선택하는 것은 약간 지루합니다. 빈 이름이나 "종료"를 입력 할 때까지 프로그램이 입력을 수집하도록 한 다음 자동으로 점수를 표시하는 것이 더 좋습니다. 텍스트 파일에서 입력을 허용하는 것이 더 좋습니다.