封装用户的数据输入

时间:2010-03-31 23:43:20

标签: c++ user-input encapsulation

对于作业,我创建了一个简单的C ++程序,它使用超类(学生)和两个子类(CourseStudentResearchStudent)来存储学生列表并打印出他们的详细信息,为两种不同类型的学生显示的不同详细信息(使用覆盖display()的{​​{1}}方法)。

我的问题是该计划如何收集用户输入的信息,如学生姓名,身份证号码,单位和费用信息(针对课程学生)和研究信息(针对研究生):

我的实现提示用户输入和收集在类本身内处理的输入。这背后的原因是每个类都知道它需要什么样的输入,所以让我知道如何要求它是有意义的(给定一个要求的ostream和一个从中收集输入的istream)。 / p>

我的讲师说,提示和输入都应该在主程序中处理,这在我看来有些麻烦,并且会使程序扩展到处理不同类型的学生变得更加棘手。

作为折衷方案,我正在考虑制作一个帮助程序类来处理每种Student类型的用户输入的提示和收集,然后主程序可以调用它。这样做的好处是学生类中没有那么多(因此它们更干净),但如果需要输入功能,它们也可以与辅助类捆绑在一起。这也意味着可以添加更多类Student,而无需对主程序进行重大更改,只要为这些新类提供辅助类即可。此外,辅助类可以替换为替代语言版本,而无需对类本身进行任何更改。

用户输入的三种不同选项(完全封装,辅助类或主程序)的主要优点和缺点是什么?

3 个答案:

答案 0 :(得分:1)

我认为老师的意思是“不要把它放在学生班上”。

弗拉德提到模特将是学生班。视图不应该在学生课程中。这个想法是学生班应该存储有关这些对象的结构信息。如何呈现数据取决于使用该类的内容。例如,如果您稍后在控制台应用程序和GUI应用程序中使用这些类,则不希望在这些类中使用显示代码。这应该取决于使用类的应用程序。

视图/控制器将位于辅助类或主程序中。在主程序中并不意味着它必须是凌乱的。你可能有很多函数来使main()看起来很漂亮和干净,但如果你在一个帮助器类中编写它也是如此。你将拥有所有这些功能,也许还有一些功能。

我建议如果这是一个小练习,除非你已经清楚地知道该课程将如何,或者你是否有时间花在搞清楚上,否则不要添加辅助课程。

答案 1 :(得分:1)

正如scv所提到的,通常最好将表示(视图)与内部结构(模型)分离。

这里有一个典型案例:

  • Student类,模型层次结构的根
  • Displayer类,另一个独立层次结构的根

显示的问题在于它根据两个元素而变化,这两个元素需要双重调度系统(使用虚拟)。

传统上使用Visitor Pattern来解决这个问题。

让我们先检查一下基类:

// 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的每个派生类中......但是否则效果很好。

优势在于显示逻辑现在与模型分离,因此我们可以添加新的显示逻辑,而无需触及我们的模型。

答案 2 :(得分:1)

虽然我对我的顾问在遗传上持怀疑态度,但我认为你的顾问在这里有一个有效的观点。

实现将cin / scanf放入类中的重要性可能过于简单了。但想象一下,你的学生班用GUI形成一些代码的后端,数据来自各种各样的东西 - 性别的单选按钮,年龄组的组合框等等。你真的不应该把所有这些都放在你的学生班里。

拥有一个“观众”或帮助学生填充学生的帮助。我建议根据视图的类型分别有一个类。您可以在main中执行此操作,但具有单独的查看器类将帮助您重用代码。

Arpan