代码之家  ›  专栏  ›  技术社区  ›  David Mason

封装类的用户数据输入

  •  4
  • David Mason  · 技术社区  · 15 年前

    对于一个赋值,我做了一个简单的C++程序,它使用一个超类(学生)和两个子类。 CourseStudent ResearchStudent )存储学生列表并打印出他们的详细信息,为两种不同类型的学生显示不同的详细信息(使用覆盖 display() 方法从 Student )

    我的问题是,程序如何从用户那里收集诸如学生姓名、身份证号码、单位和费用信息(针对课程学生)和研究信息(针对研究生)等信息:

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

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

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

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

    3 回复  |  直到 12 年前
        1
  •  1
  •   Matthieu M.    15 年前

    如前所述 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) { }
    

    如您所见,最困难的部分是,如果我希望添加一个新的派生类 学生 ,然后我需要添加一个新的 virtual 方法在 显示器 并在每个派生类中重写它 显示器 ……但除此之外效果很好。

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

        2
  •  1
  •   SCV    15 年前

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

    正如弗拉德提到的,模型将是学生班。这个观点不应该出现在学生课堂上。其思想是学生类应该存储关于这些对象的结构信息。如何呈现这些数据取决于使用类的内容。例如,如果以后要将这些类同时用于控制台应用程序和GUI应用程序,则不希望在这些类中包含显示代码。这应该取决于使用类的应用程序。

    视图/控制器将在helper类或主程序中。在主程序中并不意味着它必须是混乱的。您可能有很多函数可以使main()看起来美观、干净,但如果您将其编写在一个助手类中,情况也是如此。您将拥有所有这些功能,也许还有更多功能。

    我的建议是,如果这是一个小练习,不要添加助手类,除非您已经对该类是如何的有了明确的概念,或者如果您有时间花在计算上。

        3
  •  1
  •   Fanatic23    15 年前

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

    认识到将cin/scanf放在类中有多重要可能过于简单。但是想象一下,你的学生班用一个图形用户界面组成了一些代码的后端,数据来自各种各样的东西——性别的单选按钮,年龄组的组合框等等。你真的不应该把这些都放在你的学生班里。

    有一个“查看器”或一个填充学生的助手类可以帮助您。我建议根据不同的观点,各开一门课。您可以在main内部完成,但是拥有单独的查看器类将帮助您重用代码。

    阿尔潘