Notes for week 2

For this week's links click here.

Introduction

Last week you created a database for the Professor class and a program to maintain that database. This week you will consider the problem of designing a database for more than one interrelated classes and how to create a program that maintains the interrelationships. It will be convenient for the user to be able to see both lists and to edit each from the window containing the list.

You could use a multiple document interface, but you are essentially working with one document (multiple tables in one database), so you will use a single document interface. .

Designing a Database For a Multiclass Program

Introduction

If you are writing a problem that simply has many non-interacting classes, you have no need to do anything different than we did in week 1. You can store each class as a table in one database and not worry about relations between table elements. Applications that fall into this category are very rare. By nature most application classes are tightly related to one another. The first step in solving a large problem is to design classes with the relationship between classes specified.

Types of Relationships

If you were asked to add a course list to the problem in week one, you could add this list as if it were part of a catalog and there would be no relationship between the professor list and the course list. On the other hand if you were asked to add the list of courses for a semester, you would find that course offerings have professors and professors have courses. What types of relationships between classes are possible?

First, you will want to know how many instances of one class are associated with each instance of a given class. In the semester course list you have multiple instances of course class associated with each professor and multiple professors class instances associated with each course class instance. The following is a simplified version of the different multiplicity relations a class can have. You can also require that there be at least one or more, a specific range of items associated (for example in the case of the professor you might require 2 to 4 instances of classes) and you can also require that there be an order amoung the multiple instances.

Analyzing Our Example

If you assume that neither the number of courses associated with a Professor nor the number of Professors associated with a course has no limitations, you would get the following diagram.

How does this translate into the design of the two classes? At the analysis stage you might just add a collection of one class to each class. For example the class Professor might now be given as:

class Professor
{
public:
	// lots of functions...
private:
	String _lastName;
	String _firstName;
	String _initial;
	String _office;
	String _phone;
	int _rank;
	Array< Course> sections;
};

Similarly you could add an array of Professors to the Course class. While this takes care of the multiplicity it doesn't truly represent the relationship between the classes in this problem. First, there is no connection that directly associates a course with a professor. Close inspection of the name of the array shows that you actually have one more class derived from course offerings - these are often called sections. Now your new class Section has the following multiplicity relationships.

Implementation of Classes

This implementation is particularly well suited to relational databases like Access where you cannot store arrays in a row of a table, but can create links between items using their primary key. The relationship is entirely created in the class Section, so the only place the primary keys will appear in the class design is Section. The primary keys are maintained by the data base, so you do not want them to appear in your Professor or Course classes. Similary, the primary key for Section should not appear in the class Section. Section can be defined as follows:

class Section
{
public:
	// lots of functions...
private:
	String _sectionNumber;
	int _professor; // primary key of the professor
	int _course; //primary key of the course
};

Translating Classes to a Database

These class relations are created using Access .

Programming With Multiple Windows

Introduction

We will be responsible for creating the windows, displaying the windows and releasing memory used by the windows. If we delete a professor, there is no provision for updating the section window.

The Main Window

Child windows in an SDI appliction are just independent windows that are created at the same time the main window is created. Unless you ask that all windows remain on top (not a good idea for many reasons), you will want your main window to simply act like the top of a window (just like the main window in Borland Builder). You can do this by placing it as a bar across the top of the screen when you define it and setting its position value to "poDesigned" in the Object Inspector. Since this window will serve as the trafic cop for the other windows you should add a menu that can be hide or show the other windows. Do not force all windows to be on the screen at the same time. For now you can simply set the "Visible" property of the window. Later you will actually destroy windows not currently in use. This is a section of the main window in the program contained in week2.zip

The code executed by the choices in this menu will look like the following, where _profsVisible is a member boolean variable set to true initially.

void __fastcall TMainForm::Professors1Click(TObject *Sender)
{
   _profsVisible = !_profsVisible;
   ProfWindow->Visible = _profsVisible;
   Professors1->Checked = _profsVisible;
     
}

The Child Windows

Child windows as I indicated are just forms created the same way that the main window was created. The menu that manipulated professor data is now the main menu of the Professor window. Child windows will be displayed automatically if their Visible property is true and you include them in the Main using the "Include Unit Hdr" command in the file menu.

One way child windows differ from the main window is the close operation. For now when the close icon is clicked, the window will disappear and the menu in the main window will be adjusted to indicate that it is not visisble. The best way to do this is to call the main windows menu handler. This will be accessible to the professor window if you use the Include Unit Hdr command in the file menu to include the MainWindow.

void __fastcall TProfWindow::FormClose(TObject *Sender,
      TCloseAction &Action)
{
   MainForm->Professors1Click(Sender);

}

Working With Links in Databases

The complete declaration of the class Section is given here. Notice the _professor and _course are both links to actual records in the ProfTable and CourseTable.

class Section
{
public:
  Section(const String number="", int profLink=0, int courseLink=0);
  int operator == (const Section & s)const{return _number == s._number;}
  String completeList(TDataSet *profTable,TDataSet *courseTable)const; //complete listing of section
  int profLink()const{return _professor;}
  int courseLink()const {return _course;}
  String number()const{return _number;}
  String professor(TDataSet *source)const;
  void setProfessor(int); //Sets professor link when section edited
  String course(TDataSet *source)const;
  // Database functions
  void retrieve(TDataSet *source);
  void place(TDataSet *source);
private:
  String _number;
  int _professor; //link to professor in database
  int _course; //link to course in database
};

The Sections table you created for this program has one piece of real data, the section number, and two licks, one to the professor and the other to the course. This causes a problem when you want to display the professors name and course number in a window (as seen below). On the other hand it localizes changes in data to the table in which the data is stored.

The actual code to display this information is very simple.

__fastcall TSectionWindow::TSectionWindow(TComponent* Owner)
   : TForm(Owner)
{
   _sections.retrieve(SectionTable);
   SectionListBox->Items->Text = _sections.getDescription(ProfTable,CourseTable);
}

Notice that most of the work is done by the SectionList (Array

) function getDescription. This function must have access to both the ProfTable and the CourseTable since it needs to retrieve the professor's name and the course number. This function creates an iterator and calls on a getDescription function for each course. Both functions are seen here. I could have used the Lookup function to find the course and professor, but choose instead to create my own function findMyId.

String SectionList::getDescription(TDataSet *profTable,TDataSet *courseTable)
{
   String result="";
   ArrayIterator
next(*this); while (next) { result = result + next().completeList(profTable,courseTable)+'\n'; next++; } return result; } String Section::completeList(TDataSet *profTable,TDataSet *courseTable)const { return _number +"\t\t"+course(courseTable)+"\t" + professor(profTable); } bool findMyId(int key,TDataSet *source) { source->First(); while (!source->Eof) { if ( source->FieldByName("Id")->AsInteger == key) return true; source->Next(); } return false; } String Section::professor(TDataSet *source)const { if (findMyId(_professor,source)) { Professor p; p.retrieve(source); return p.name(); } else return ""; } String Section::course(TDataSet *source)const { if (findMyId(_course,source)) { Course c; c.retrieve(source); return c.number(); } else return ""; }

The Repaint function

Windows frequently overlap other windows and are often updated because of actions in other windows. For example the SectionWindow would be updated if a professor's name changed (marrage). The function Windows calls when it must redraw a window in Borland Builder is Repaint. This is a virtual function (and must be declared as such). Our section window must have a Repaint function since it is updated in response to actions in other windows. In this case the repaint is a direct copy of the constructor for the window.

void __fastcall TSectionWindow::Repaint(void)
{
   _sections.retrieve(SectionTable);
   SectionListBox->Items->Text = _sections.getDescription(ProfTable,CourseTable);
}

Editing a Section

When creating a new section or editing an existing section, you want to have complete listings of professors and courses available to the user. In the case of editing you want the current information to be visible in the dialog used for editing. The code for editing a section is far more complex than that for adding a section (you can see that by downloading the complete code below). You need to know the position of the professor and the course in the database in order to set the ItemIndex in the two comboboxes. I used Lookup to retrieve the three parts of the name of the professor in the selected section. From this I created a Professor which could be found in a local professor list. I used a similar trick for the course. Notice that Lookup returns a variable of type Variant. In the case where you requesto more than one field value, Variant is an array with each requested item in the position deletermended by the order the fields were requested in. Use GetElement to retrieve the values. All you needed for the course was its number. In this case the Variant was a simple String. The rest of this code is very similar to that covered last week .

void __fastcall TSectionWindow::EditSection1Click(TObject *Sender)
{
   int where = SectionListBox->ItemIndex;
   if(where >=0)
   {
      ProfList profs;
      profs.retrieve(ProfTable);
      CourseList courses;
      courses.retrieve(CourseTable);
      SectionDialog->ProfComboBox1->Items->Text = profs.getNames();
      long int profId = _sections[where].profLink();
      Variant v = ProfTable->Lookup("Id",profId,"lastName;firstName;initial");
      Professor p(v.GetElement(0),v.GetElement(1),v.GetElement(2));
      int profLoc = profs.find(p);
      SectionDialog->ProfComboBox1->ItemIndex = profLoc;
      SectionDialog->CourseComboBox1->Items->Text = courses.getNumbers();
      long int courseId = _sections[where].courseLink();
      Variant v1 = CourseTable->Lookup("Id",courseId,"number");
      Course c(v1);
      int courseLoc = courses.find(c);
      SectionDialog->CourseComboBox1->ItemIndex = courseLoc;
      SectionDialog->SectionNumber->Text=_sections[where].number();
      if(SectionDialog->ShowModal()==IDOK)
      {
         ProfTable->First();
         ProfTable->MoveBy(SectionDialog->ProfComboBox1->ItemIndex );
         int profId = ProfTable->FieldByName("Id")->AsInteger;
         CourseTable->First();
         CourseTable->MoveBy( SectionDialog->CourseComboBox1->ItemIndex);
         int courseId = CourseTable->FieldByName("ID")->AsInteger;
         Section s(SectionDialog->SectionNumber->Text,profId,courseId);
         SectionTable->First();
         SectionTable->MoveBy(where);
         SectionTable->Edit();
         s.place(SectionTable);
         SectionTable->Post();
         SectionTable->Active=false;
         SectionTable->Active=true;//This refreshes the table variable from the database. 

         _sections.deleteItemAt(where);
         _sections.insertItemAt(s,where);
         SectionListBox->Items->Text = _sections.getDescription(ProfTable,CourseTable);
      }
   }
}

Right-Button Menu

Last but not least you will see that in the SectionWindow, you may press the right button and it will select the section you are over and cause the section menu to pop up. If you double click on an item it will automatically start the edit function. If you select on double click from the Object Inspector event pane to create a handler, simply call on EditSection1Click from this function. To see how to implement the right-button list box click here .

Complete Code

To get the complete code click here.