Инкапсуляция пользовательского ввода данных для класса


Для задания я сделал простую программу на C++, которая использует суперкласс (Student) и два подкласса (CourseStudent и ResearchStudent) для хранения списка студентов и распечатки их деталей, причем разные детали показаны для двух разных типов студентов (используя переопределение метода display() из Student).

Мой вопрос о том, как программа собирает входные данные от пользователя таких вещей, как имя студента, идентификационный номер, единица измерения и информация о плате (для студента курса) и исследовательская информация (для студентов-исследователей):

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

Мой лектор говорит, что все запросы и ввод должны обрабатываться в основной программе, которая мне кажется несколько запутаннее, и было бы сложнее расширить программу для работы с различными типами студентов.

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

Каковы основные преимущества и недостатки трех различных вариантов пользовательского ввода (полностью инкапсулированный, вспомогательный класс или в основной программе)?
3 4

3 ответа:

Как упоминалось в scv, обычно лучше отделить представление (представление) от внутренней структуры (модели).

Вот вам типичный случай:

  • класс Student, корень иерархии модели
  • класс Displayer, корень другой независимой иерархии
Проблема с дисплеем заключается в том, что он варьируется в зависимости от двух элементов, что требует системы двойной отправки (с использованием виртуального).

Это традиционно решается с помощью Шаблон Посетителя .

Давайте сначала проверим базовые классы:

// student.h
class Displayer;

class Student
{
public:
  virtual ~Student();
  virtual void display(Displayer& d) const = 0; // display should not modify the model
};

// displayer.h
class Student;
class CourseStudent;
class ResearchStudent;

class Displayer
{
public:
  virtual ~Displayer();

  virtual void display(const Student& s) = 0; // default method for students
                                              // not strictly necessary
  virtual void display(const CourseStudent& s) = 0;
  virtual void display(const ResearchStudent& s) = 0;
};

А теперь давайте реализуем некоторые:

// courseStudent.h
#include "student.h"

class CourseStudent: public Student
{
public:
  virtual void display(Displayer& d) const;

};

// courseStudent.cpp
#include "courseStudent.h"
#include "displayer.h"

// *this has static type CourseStudent
// so Displayer::display(const CourseStudent&) is invoked
void CourseStudent::display(Displayer& d) const
{
  d.display(*this);
}


// consoleDisplayer.h
#include "displayer.h"

class ConsoleDisplayer: public Displayer
{
public:
  virtual void display(const Student& s) = 0; // default method for students
                                              // not strictly necessary
  virtual void display(const CourseStudent& s) = 0;
  virtual void display(const ResearchStudent& s) = 0;
};

// consoleDisplayer.cpp
#include "consoleDisplayer.h"

#include "student.h"
#include "courseStudent.h"
#include "researchStudent.h"

void ConsoleDisplayer::display(const Student& s) { }

void ConsoleDisplayer::display(const CourseStudent& s) { }

void ConsoleDisplayer::display(const ResearchStudent& s) { }
Как вы можете видеть, трудная часть заключается в том, что если я хочу добавить новый производный класс Student, то мне нужно добавить новый метод virtual в Displayer и переопределить его в каждом производном классе Displayer... но в остальном все работает отлично.

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

Я думаю, что ваш учитель хотел сказать: "не ставьте его в ученические классы".

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

Представление / контроллер будет находиться в вспомогательном классе или основной программе. Пребывание в основной программе не означает, что она должна быть грязной. У вас может быть много функций, чтобы сделать main() красивым и чистым, но то же самое будет верно, если вы напишете его в вспомогательном классе. У вас будут все эти функции и, возможно, еще несколько.

Я бы предположил, что если это небольшой упражнение, не добавляйте вспомогательный класс, если у вас уже есть четкое представление о том, как этот класс будет выглядеть, или если у вас есть время, чтобы потратить на выяснение.

Хотя я генетически скептически отношусь к своим советникам, Я думаю, что у вашего советника есть веская точка зрения.

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

Имея 'viewer' или вспомогательный класс то, что заселяет студента, помогает. Я предлагаю иметь класс каждый в зависимости от вида зрения. Вы можете сделать это внутри main, но наличие отдельных классов просмотра поможет вам повторно использовать код.

Арпан