Derece Hesaplayıcı (Bir önceki OOP Sürümü)

Dec 19 2020

Bir OOP düşünme tarzı geliştirmeye çalışıyorum. Birinin Not Hesaplayıcı OOP programımı gözden geçirmek için değerli vaktinin bir kısmını ayıracak kadar nazik olacağını umuyordum. Her zaman olduğu gibi, neyi iyi yaptığımı, neleri geliştirmem gerektiğini ve sahip olduğum şeyleri nasıl geliştirebileceğime dair herhangi bir öneriyi bilmek isterim. Bu arada, Class adında bir sınıfım var. Kafamı karıştırmamak için muhtemelen "cls" ile başlamalıyım. Bu programa girilmesi gerektiği gibi davranın, hatayı kontrol etmedim. Bu programın amacı OOP'de geliştirmektir.

   // 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();
   
}

Yanıtlar

6 Edward Dec 19 2020 at 09:35

İşte programınızı geliştirmenize yardımcı olacak bazı şeyler.

G / Ç'yi veri işlemleriyle karıştırmayın

Genellikle sahip konuşma std::getlineve std::coutgibi bir veri sınıfı içinde Classiyi bir fikir değildir. Sınıfın yeniden kullanılmasını zorlaştırır. Daha iyi uygulama (veri tutmaktır Student, Classvs.) kullanıcılardan giriş almak ayrı. Model-View-Controller tasarım deseni böyle programlar için çok kullanışlıdır. Bu cevaptaConsoleMenu olduğu gibi sınıf gibi bir şey kullanmayı düşünün .

Pratik olduğunda hareket semantiğini kullanın

In AddStudentfonksiyonu, bu çizgi var:

students.push_back({ name, scoreGot });

emplace_backDerleyiciye oluşturup kopyalamasına gerek olmadığını, ancak nesneyi yerinde oluşturmanın güvenli olduğunu söyleyen hangisinin kullanılması daha iyi olur .

Sınıf arayüzünü yeniden düşünün

ClassSınıfı bu üye işlevi vardır:

std::vector<Student>& accessStudents() { return students; }

Bu kötü bir fikir. Dahili bir sınıf üyesine bir başvuru döndürür. ClassÖrnek silinirse ne olacağını düşünün , ancak bazı harici varlıklar artık mevcut olmayan verilere referans tutmaya devam ediyor. Kullanıldığı tek yer içeridedir GradeCalculator::averageGrade()ve GradeCalculator::DislayResult()bu, sınıf arayüzünde bir şeylerin doğru olmadığının güçlü bir göstergesidir. Bu averageGrade()işlevi Classüye işlevi yapmayı öneririm .

Mantıklı olduğunda varsayılanları kullan

StudentYapıcı esas derleyici tarafından üretilmiş olan olan ile aynıdır. Hata olasılığını azaltmak için basitçe ortadan kaldırılabilir.

Kullanıcı hakkında düşünün

Programdan zarif bir şekilde çıkmanın açık bir yolu yoktur. Bunun için bir menü öğesi eklemenizi öneririm. Ayrıca, programı gerçekten kullanan herhangi birinin tüm öğrenci sınıfına girmek ve ardından tüm sınıfın puanlarını hesaplamak isteyeceği ihtimali de vardır. Tekrar tekrar "1. Öğrenci ekle ..." seçeneğini seçmek zorunda kalmak biraz sıkıcıdır. Daha iyisi, programın boş bir ad veya belki "çıkma" girilene kadar girdi toplaması ve ardından puanları otomatik olarak göstermesi olabilir. Daha da iyisi, bir metin dosyasından girişe izin vermektir.