Object Oriented (OO) applications promote re-use of ready-made objects in various
projects. Let's take a look at a conseptual example first, then show how it could be implementerd.
Your company has to get a set of
CGIs to survey users on a new product you just released (Whammo Widgets!). You can just
write the HTML code with forms, then a small CGI to get the data and save it. You are done.
Now what if your boss comes by and informs you that they now have a new line of add-ons to the
Whammo Widgets that make them better. These add-ons are only used by a small group of users who
demand that extra feature (an 11 on the volume dial if you must).
Now the dilemma begins, how do you survey different customers about their needs? An easy way
would be to write more HTML and more CGIs and be happy. But your boss is never happy and will
have more products.
This is where OO design is important.
We will now dive right into developing the Object Oriented Common Gateway Interface (OOCGI) Applications.
Let's first begin with an object called WhammoCompany
and an object to be used for surveys:
Please note that the code provided is in segments to concentrate on important aspect of the design and is far from complete
class Survey { public: Survey(const char *pccUsername); virtual void display(AStreamOutput *posOut) = 0; private: char *m__pccUsername; //Used to customize your surveys to each user }; class WhammoCompany : public ACGI { public: void display(Survey &survey); };
Nothing too tricky right! You now have a company object and a survey object. Your survey object is pure virtual, which means you will probably have many of them which will override the display() method.
We also know that is a common header and a footer that you use to show logos, banners, company e-mails and such. Let us add that
functionality:
class WhammoCompany : public ACGI { public: void displayHeader(); void displaySurvey(Survey &survey); void displayFooter(); }
Let's take a look at a sample implementation of WhammoCompany object so far:
void WhammonCompany::displayHeader() { mimeHTML(); //First ever thing to a browser is a MIME directive htmlStartHTML(); //Start the HTML page htmlDoHEAD("Whammo Company Survey"); //Do the HEAD and TITLE in one htmlStartBODY(0xF0F0F0, //White background 0x101010, //Black text 0xFF0000, //Red link 0xFF0000, //Red visited link (links don't change color after visit) 0xFF00FF, //Activated link changes color "images/bkground.jpg" //Background image ); htmlStartTag("H1", "ALIGN=CENTER"); //<H1 ALIGN=CENTER> outStringN("Whammo Company"); //This method is defined in AStreamOutput htmlEndTag("H1"); //</H1> } void WhammoCompany::displayFooter() { //Display footer info htmlStartTagEx("H5", "ALIGN=CENTER") outStringN("For more help please email "); htmlDoTagEx("A", "HREF="mailto:webmaster@whammo.com", "our webmaster"); htmlEndTag("H5"); //End the BODY and HTML tags htmlEndBODY(); htmlEndHTML(); } void WhammoCompany::displaySurvey(Survey &survey) { survey->display(this); }
There is an interesting thing happening here. Each survey object will use this to output itself correctly.
Naturally next we should look at the survey object and see how we would implement them. Let's
say we have 2 surveys, basic and advanced:
class BasicSurvey : public Survey { void display(AStreamOutput *posOut) { *posOut << "This is a basic survey.
" << endl; } }; class AdvancedSurvey : public Survey { void display(AStreamOutput *posOut) { *posOut << "This is an advanced survey.
" << endl; } }
The body was inlined for clarity, your display method will be a bit more complex I hope.
We now have declared (and implemented) 4 objects. Let us assume that our CGI will be called
as follows:
http://www.whammo.com/survey.cgi - to specify which survey the use wants
http://www.whammo.com/survey.cgi?Survey=Basic - for a basic survey
http://www.whammo.com/survey.cgi?Survey=Advanced - for advanced survey
Now see how we can use them in a CGI:
NOTE: the lower case prefix in freeCGI is an abbreviation of the class that
the method was inherited from:
Example:
plGetValueByName() is from APairList and retrieves VALUE from given NAME in a pair; (NAME=VALUE)
cgiGetFormItems() is from ACGI class and collects and parses all NAME=VALUE pairs using AFormList class
//Include ACGO++ #include "a_acgi.h" //Inclulde your objects: WhammoCompany, Survey, BasicSurvey and AdvancedSurvey #include "whammo.h" int main() { WhammoCompany whammoObject; //This iwill create the correct association to ostream as would ACGI object //Let's display a common header first whammoObject.cgiGetFormItems(); //Get all FORM items that were passed to this CGI //Now let's figure out what the user wants to do //ACGI is made up of 2 objects AHTML and AFormList //AHTML is used for output only and AFormList is a FORM item processor //We will get the pointer to VALUE of "Survey" or NULL if not found const char *pccValue = whammoObject.plGetValueByName("Survey"); if (pccValue == NULL) { //a_User was not asked what survey to use whammoObject.cgiStartFORM(); //Create a FORM tag with reference to itself //Quick FORM (this is pretty ugly but you can have a much nicer looking form :) whammoObject << "Enter name: <INPUT TYPE=TEXT NAME=\"Username\" VALUE=\"Anon\">" << endl; whammoObject << "Survey type: <SELECT NAME=\"Survey\"> <OPTION>Basic<OPTION>Advanced</SELECT>" << endl; whammoObject << "<INPUT TYPE=SUBMIT>" << endl; } else { if (strstr(pccValue, "Basic") == 0) { //Basic survey request found BasicSurvey survey(whammoObject.plGetValueByName("Username")); whammoObject.display(survey); } else if (strstr(pccValue, "Advanced") == 0) { //Advanced survey request found AdvancedSurvey survey(whammoObject.plGetValueByName("Username")); whammoObject.display(survey); } else { //Unknown whammoObject << "I do not understand!<BR>" << endl; } } //Now show a common footer whammoObject.displayFooter(); return 0; }You now have a company object that can be reused as well in other CGIs even if they do not need surveys. Using this ideology you can build objects that can be placed into a company library and used by future CGI applications. I will not go into further discussion on OO, that could be found in many C++ and Smalltalk texts.
Here are some hints on using ACGI:
But most of all, have fun doing it!