This should make you appreciate why people tell you to comment code. Notice that the code you've been provided is only lightly commented. That probably makes your life very tough. What should you do? Should you think "if they don't tell me how it works, then I shouldn't be forced to learn it"? Unfortunately, that's not the attitude that will lead you to a successful career.
To help you understand the idea, there once was a student (who's grades were OK, not spectacular, not bad) who was taking CMSC 424. The TA gave directions on how to install a server for a database. He was following the directions, and was unable to get it to work. So, what did he do? Did he email the TA and say "your stupid directions were wrong"? No.
Instead, he knew that other students had gotten it to work, so he began to investigate. He knew it wasn't completely broken, because a few things seemed to work. He tried to determine what was and what was not working. He ran some experiments, so that if he sent information to the TA, he could indicate what was going right and what wasn't.
He also read the files that were in the directory when the server was set up, to see if they had any meaningful information. He finally restarted the server on a different machine, and that worked.
This story is great because it shows the need for programmers to be resourceful, even when things aren't going so well. Have you ever seen students who seem to know what to do, and you're struggling? How do they do that? Some of that is just this kind of resourcefulness. Being able to learn how to make progress, even when you're confused is a very useful skill.
In this project, you were given code, with all sorts of strange things going on. Event driven programming? Listeners? Buttons? What are you supposed to do with all that? Yet, you were provided files, and those files are simply in C++. Worse gets to worse, you read the code, and you begin to trace.
You had input files, you had a project description. Try to follow what the code is doing. This means running it. This means putting your tracing skills to work. Add debugging statements, and see what happens. It may be weird, but when you're given little information, that's what you need to do.
One surprising thing you'll discover. It's unnecessary to know how everything works to write code. However, it does help to know a big picture of what's going on. This is not such a surprising idea. After all, do you have any idea how the input goes from the screen into your code? Do you have any idea how "cin" reads and parses your input? You may know exactly how it's implemented, but you do know what it's supposed to do, and that's good enough for you to begin programming.
Basically, the idea for the project comes from two sources. First, is www.livejournal.com, which is a website where users can store and update "blogs" (weblogs) which are online journals or diaries.
The second, which is completely unrelated to the first, is Java graphical user interfaces. We're going to concentrate on the second.
In a GUI (graphical user interface), there are GUI components. These components are often called widgets. Examples of widgets include buttons, menu options, radio buttons, text fields, scrollbards, slider bars, etc. Anything you interact with in a GUI is considered a widget.
When you interact with a widget (for example, pressing a button) an "event" is generated. The "event" is handled by code that's called an "event handler". In Java, these event handlers are called listeners. We will emulate such listeners in C++.
While graphical libraries have been written for C++ (Visual C++ libraries, OpenGL, X Windows, etc), none of them are considered part of the language in the same way that Java's graphical libraries are considered part of Java. Also, X Windows is quite a bit more complicated than Java, even though it gives a lot of power too.
In any case, we couldn't easily grade a real GUI project even if we had such libraries. With a class this size, and with only a small number of graders, it would take too much time to get the project adequately graded.
Instead, we emulate GUI programming using text input (this is where you look at the simple text input provided in the posting account).
To make your life simple (and ours), there's only two widgets you work with: buttons and text fields. A button is something you "press". A text field is something you enter text in.
All of this is already taken care of by main() (although you may need to modify main() to produce the printed output required)
Here are the three commands in the input file.
The typical input file consists of a "user" inputting text using SET_TEXT and then pressing a button.
The input file is read in via main(). When SET_TEXT is read in, main() finds the "text field" object (see setText() in main()) and places the text in the widget. Setting text does not generate an event.
When QUIT is read in, the program exits.
When PRESS_BUTTON is read in (see pressButton()) the name of the button is passed to the event manager object by the method "generateAction()". This in turn calls runListeners() on the button object itself. Each button stores 0 or more listener objects. All listeners associated with the button have their action() method "run".
Each listener is an object with the method action(). So, runListeners() calls action on each listener object associated with the button. To associate a listener with a button, you must add the listener to the button (see later on for details).
This class contains text fields and buttons. Right now, it only contains two Button pointers and one TextField pointer. All of these are just "test" buttons and text fields. You should replace them with the text fields and buttons specified elsewhere in the project.
The constructor for Gui is called early on in main(). In a real GUI application for Java, you would derive a class from Frame add Panels to the Frame and place various widgets in the panels. You would then add listeners to objects that produce events.
Similarly, in Gui you create the widgets in the constructor (see the Gui() constructor for an example). There are two kinds of widgets to create.
There are two steps involved. First, create the text field object.
name = new TextField( "name" ) ; // be careful!!! about the name
It's important that you give the text field the name specified
by the project description (case-sensitive). If you don't,
then the code in main() won't be able to locate the
TextField object.
Second, add the widget to the Gui.
addWidget( name ) ;
The purpose of this (in case you're curious---in that case, you
should have traced the code) is to let the event manager know about
the text field.
Don't forget to call addWidget!.
There are three steps involved. First, create the button (see how TextField is created), then call addWidget() on the button (just as in TextField). Finally, you need to add a listener object, which will be called when the button is pressed.
Adding the listener object is how you make the button do something meaningful.
There are three ways to create a listener object.
Advantages? It's flexible. If you want to add a new listener, you create a new class.
Disadvantage? You must write lots of classes, which all do roughly the same thing. To avoid such a problem, write all the headers in Listener.h and write the implementation all in Listener.cpp. That way, you save in the total number of files.
In the Gui constructor, you add the listener
as such:
// create the listener
// -- pass "this" so that listener has access to Gui object
AddJournalListner * list = new AddActionListener( this ) ;
// adding listener to button
addJournalButton->addActionListener( list ) ;
void MyActionListener::action( Button *buttonPtr )
{
if ( buttonPtr->getLabel() == "apply" )
apply() ;
else if ( buttonPtr->getLabel() == "postJournal" )
postJournal() ;
// ...and more
}
Advantage? One class, easy to write. Easier to trace
code. However, you need to rewrite code if you want to
change behavior. Sometimes quicker to change listeners
instead.
Disadvantage? It's a little slower. You must search through all the if-else statements. A little harder to switch from one listener to next.
You add the action listener in the constructor of Gui in the same way as the previous method.
class Gui : public AbstractGui, public ActionListener {
// implement action() in Gui
};
The reason for using multiple inheritance is that you will
need access to infomration in Gui. See next section
for more information.
Adding the listener is a little different. First, there's
no need to create the listener since Gui is already
a listener. Second, you add the "this" object to the button.
Here's an example:
// adding listener to button---no need to create a listener
// since Gui is already a listener
addJournalButton->addActionListener( this ) ;
If you use this, then you are basically planning to
implement action() with a large if-else statement
to handle the buttons.
Here's how the constructor might look, with a parameter
for passing Gui.
MyActionListener::MyActionListener( Gui * ptr )
{
gui = ptr ; // gui is a data member of type Gui
}
Realize that Gui will need to have enough methods so
that the action listener can update the Gui.
You should add information in one of two ways. Either add data members to Gui, or create a DataManager and put your data members there (then put an instance in Gui). The first may be easier since there are limited data members.
There are two main reasons why some of you may be afraid of writing classes. First, you just don't know how to start and what should happen. Second, you are afraid of making the wrong choices. The first reason is, of course, a big problem. If you don't know what's going on, it's hard to make progress. The second is less of a problem. Sure, everyone would like to save time by making the perfect decision the first time around. Everyone would like to avoid writing a class, and deciding that it was a bad way to write the class, so the entire work has to be scrapped and rewritten. Yet, that's one of the few ways you learn how to write code---by making mistakes. It's time consuming, it's frustrating, but it saves time later on (must later on, alas).
Here's a hint. First, you are writing journals and MP3s. What do they have in common? Date. So that should go into AbstractEntry. You should define relational operators for Date to compare them.
Second, you may want to have a Person. The person stores their name, alias, a list of AbstractEntry * objects (journals and MP3s), a list of friends, and their status (GOLD, SILVER, BRONZE).
Third, since there can be more than one person, you may wish to have some list (array list, sorted list, etc) in the Gui object as a data member.
That's all you really need.
But what strategy should you use, if you hadn't been given those hints? Suppose you had to do a different project altogether? For example, you want to do "instant messaging"?
The answer is to sit down and think about what actions are performed. What buttons will be pressed by the user? Pick a button, and decide what will happen when that button gets pressed. Ask the following questions:
Try to detail out the steps of what will happen. This may help you decide what objects to create.
This forces you to think about what data structure to use. You may think "I don't know", then pick something simple like an array, and work from there. What information should go in a "journal" object? Put the minimum you need now, and add more later.
When you look at the class you've written, start to ask yourself, "Could I use this class in a different situation? If I gave this class and had to "sell" it, would anyone buy it?". For example, is it important to have relational operators? How about print methods? How about input methods?
Here's a good example. Suppose you are writing a string class. The current project says you need to determine whether a string is a palindrome. Should the string class have an isPalindrome() method. You might say "yes" because the project needs it, but in general, does anyone need that method? You might say "yes, it's possible they will need it". However, if you add every possible method a user might need, you will have thousands of methods, which makes the class very difficult to use.
Instead, you should leave out this function, and if you REALLY need it, use inheritance, and add a isPalindrome method there, for special purpose uses.