Contents  1-5  6-11  12-16  17-21  22-27   28-33  34 - 38  39-46  Projects   MFC

   Contact
   Search
   C
   C++
   Visual Basic
   Java
   JavaScript
   DHTML
   Style Sheets
   About
   Active X
   TDC Binding
   PHP
   Perl and CGI
   Flash
   XML
   SQL
   Messages
   Chat
   MCSE
   Linux
   Cabling   
   ActionScript
   Downloads
   E-Cards   
 
    
    

Design?  Hmmm - that's an interesting topic.  Programming, at its core, is problem solving.  Due to the nature of the
situation, when you design a program, your critical thinking and planning skills will be much more important than your
coding skills.  You will always be doing something you haven't done before.  You will always be tackling a fresh and
complex problem. 

"Just remember: you're not a "dummy," no matter what those computer books claim.  The real dummies are the people who,  though technically expert,  couldn't design hardware and software that's usable by normal consumers if their lives depended upon it!"  - Walter Mossberg

It becomes a logical conclusion, after observing the current market place, that technology is not what sells software, but rather, psychology.  Many times there are applications available on the market that are far more powerful and versatile than those of their competitors.  However, these applications do not succeed because their interface is not as intuitive.  It boils down to the size of the learning curve and the amount of effort it will take the user to become accustomed to a new program.  You can never, ever underestimate the importance of the user interface. 

Menus, text, boxes, graphics, multimedia, and whatever other components make up the user interface must be arranged in a fashion that is intuitive and responsive.  The program should be able to handle a good deal of abuse, as users will never do exactly what a program asks them to do.  An application should be forgiving of errors and as user-friendly as possible.  An intuitive interface will be just as important as any bells and whistles the application may offer.

Which language will you choose to build the project in, and why?  Does the project need the speed and efficiency of C++?  Does it need the platform independence of Java?  Does it need the speed of development that can be achieved with Visual Basic?  As far as planning goes, you might choose to use a flowchart, a graph, a table or pseudo code.  You might use all of these tools.  You might plan with Word, Visio, or simply sketch out your thoughts on paper.  There is no "right" way to plan.  Use what works for you - we are all different and therefore we all respond to different methods.  Some planning methods:

  • Interview - Start with an interview.  The best solution is to take a tape recorder and record your conversation.  If you can't do this, then take detailed notes when meeting with the client.  The most common mistakes result from miscommunication.  Before you start to pseudocode, flowchart or code anything, you must be ABSOLUTELY sure of what your client wants and how they want it.  Get it in writing, and if this stage will involve a lot of time on your part, be sure you reflect that in your estimate of the project.  If you are creating the project for yourself - interview yourself!   No, I don't mean have an out-of-body experience.  :(   You know what I mean here.  :)
  • Flowcharting - Now that you have some basic information about what kind of program is required, and what information it will have to provide, you need to begin to organize your thoughts.  Look at the IPO (Input Output Processing).  Plan your program's behavior, step by step, charting decision structures, repetition, input, output and processing.  Ask yourself, if the user does this, then what?  If my program receives this type of input, then what?   Visio is an excellent tool for planning.  Word, WordPerfect and Star Office also have good tools for the planning and flow-charting stage.
  • Pseudocode - Pseudo coding should not be language specific, though many times people use a C++ or C-ish style when planning.  The idea behind pseudocode is to simply sketch the processes and behaviors that will need to take place, irrespective of the language chosen.  You focus on logic, behavior and flow, rather than coding.
  • Coding - Some argue that coding a project should take the least amount of time (40% - 20% of total project time).  While planning will most probably consume the greatest amount of time, coding can present its own share of challenges and problems.  This maxim will be violated many, many times as unexpected changes and modifications arise.  The one thing to remember is that,  the more thoroughly you plan the project, the smoother the coding process will be.

You will never be the all-knowing, omniscient computer oracle that you aspire to be.  A dose of humility can save you a lot of embarrassment.  People who project computer guru personas are ultimately false, and often hide their insecurities by criticizing the ignorance of others.  You will never have all of the answers, and there is no reason why you should feel like you have to.  The trick of the trade is, you simply need to learn where you can find the answers.  You will need to place at your disposal many sources of information:

  • A Book Library - Nothing beats a good library of books on programming theory and programming languages.
  • Class notes - Keep your notes and handouts from previous computer classes and courses.
  • A portfolio - Keep a portfolio of all the projects you have worked on.  Keep a portfolio on CD-R, a laptop, or your desktop of all the projects you work on.  Chances are, you will be able to use some of the code and structures from your past projects in your new ones.
  • A scrapbook - Keep a scrapbook of favorite routines and algorithms - this can be very helpful.
  • Internet Access - probably one of your most powerful tools.  If you can't figure out how to code something, chances are you can find out by researching the topic over the internet.  There are thousands of programming reference sites for you to bookmark.
  • Email Contacts - Keep contact information on your fellow students, fellow employees and instructors.  Sometimes an acquaintance is just the resource you need to help you tackle a problem in a different way, or point out a completely alternative solution.

We will not go into all the gory details of systems analysis in this section (aren't you grateful?), but there must be a method to the madness.  Remember, one of the most difficult aspects of beginning a programming project is the planning stage.  What is the nature of the program? 

Database Game
  • 1st - Gather information.  Find out exactly what kind of information your database will need to store and how it will be required to present it.
  • 2nd - Plan your classes, structures and relationships with tables, charts, and pseudocode.  Practice normalization (1 NF, 2NF, and 3NF).
  • 3rd - Decide on a language.  Visual Basic, C++. SQL queries, Access, Java?
  • 4th - Design the user interface, the back end, and the intermediate structures.
  • 1st - What will the plot of your game be?  What motivation does the player have?
  • 2nd - Create the interface.  Paint the scene, instantiate the environment where the action will take place.  Whether it is console based or it uses the GUI, once you define the environment, you may then code how the player will interact with it.  This involves populating the game with objects.
  • 3rd - Create the flow.  Plan each possible decision of the player.  Allow the options of what the player can do to change based on each decision she/he makes.  You must code your game so that the player can interact with the objects and the objects with the player.  The player itself is an object, so you might include accessor methods for object interaction.

Questions you might ask yourself or a client:
1. Do I really need customized software? 
2. Is there some other application that already does this? 
3. Can I modify a third-party application, or do I need to produce an application from scratch?
4. Do I truly understand the nature of the problem and EXACTLY what the client wants?

Simulation Technique - A simulation is a computer model of a real world system.  A good design using the simulation technique starts with what questions you hope the simulation will answer.  There are four phases:

1. Conceptualization phase - Ask what the customer hopes to gain from the program.  What is it for?  What questions must it answer?  Think about what is inside and outside of the program.
 
2. Analysis phase - Help the customer understand what he requires from the program.  What behavior will the program exhibit? What kinds of interactions do the customers expect?

3. Use case - A series of documents, a description of how the system will be used.  It describes interactions and use patterns, helping the programmer capture goals and requirements for the system.

4. High level design - Don't be concerned about the language, platform or operating system.  Focus on how the system will work. What are the major components?  How do they interact with each other?  Set aside issues relating to user interface and focus on components of the problem space.  Think of the responsibilities of objects, what information they hold, how they will collaborate and interact with other objects.  You may set up a problem space - a set of problems and issues the program is trying to solve.  You may also set up a solution space - a set of possible solutions to those problems.

Example #1: Design an alarm simulation:

The Scenario: Ormond Beach City Hall has hired you as a consultant and programmer to create custom software to combine their fire and theft detection systems.  You do a lot of research on alarm systems.  You interview employees and key people thoroughly.  You find out EXACTLY what their current two systems do, and you ask the right questions to determine what it is they want their new, combined system to do.

The Sensor Object:  After the initial interview and some research, you decide that the basic object of this program will be a sensor.  There are many derivatives of a sensor - trip, window, door, smoke, and sound sensors.  You therefore decide to make "Sensor" your base class, and all other "Sensor" objects will derive from this base class.  The base class, "Sensor" represents many different kinds of sensors, so "Sensor" would make a good ADT.  As such, it would provide a complete interface for all types of Sensors, and each derived type would provide the implementation.  Clients could use each type of Sensor without regard to which type they were or how they worked.  The ADT should handle all the needs of each derived class.  You will need a complete understanding of what sensors do, that is their interactive behavior, as opposed to how they work. 

The Other objects:  The alarm, the program, keeping a log, a timer object, etc.  These will all interact with "Sensor" objects, based upon the information you have gleaned.  What are the classes?  If you have a class "HeatSensor", it will have to derive from "Sensor".  If it makes periodic reports, it may also derive via multiple inheritance (polymorphically) from the Timer class, or it may have a Timer as a data member.  Practice both encapsulation and data hiding.   Now that we have some of the details planned out, we turn to design goals>

Incorporating OOP Philosophy into the Design:

Encapsulation - Each class should have a coherent and complete set of responsibilities that no other class has.  "DO one contained thing, and do that thing well."  Example:  While a Sensor class may note and log the current temperature, it may delegate the responsibility of recording that data to a Log object.  Maintain a FIRM division of responsibilities. Changes to one class should not affect others.

Virtual methods - Should HeatSensor have a ReportAlarm() method?  All sensors will need the ability to report an alarm.  This is a good indication that ReportAlarm() should be a virtual method of the base class Sensor, and that Sensor may be an abstract base class.  The overridden ReportAlarm() methods from each derived class will fill in the details they are uniquely qualified to supply.

Event loops
- Typically, infinite while(true) loops will be used that get messages from the OS (mouse clicks, keyboard) and dispatch them one by one, returning to the loop until an exit condition (sentinel value) is satisfied.  Example:

#include <iostream.h>
//-------------------------------------------------------------------------------------
class Condition
{
      public:
      Condition() { }
      virtual ~Condition() {}
      virtual void Log() = 0;
};
//-------------------------------------------------------------------------------------

class Normal : public Condition
{
      public:
      Normal() { Log(); }
      virtual ~Normal() {}
      virtual void Log() { cout << "Logging normal conditions...\n"; }
};

//-------------------------------------------------------------------------------------


class Error : public Condition
{
      public:
      Error() {Log();}
      virtual ~Error() {}
      virtual void Log() { cout << "Logging error!\n"; }
};

//-------------------------------------------------------------------------------------


class Alarm : public Condition
{
      public:
      Alarm ();
      virtual ~Alarm() {}
      virtual void Warn() { cout << "Warning!\n"; }
      virtual void Log() { cout << "General Alarm log\n"; }
      virtual void Call() = 0;

};

Alarm::Alarm()
{
     Log();
     Warn();
}

//-------------------------------------------------------------------------------------

class FireAlarm : public Alarm
{
      public:
      FireAlarm(){Log();};
      virtual ~FireAlarm() {}
      virtual void Call() { cout<< "Calling Fire Dept.!\n"; }
      virtual void Log() { cout << "Logging fire call.\n"; }
};

//-------------------------------------------------------------------------------------


int main()
{
    int input;
    int okay = 1;
    Condition * pCondition;

    while(okay)
    {
       cout << "(0)Quit (1)Normal (2)Fire: ";
       cin >> input;
       okay = input;

       switch(input)
       {
          case 0:  break;
          case 1:  pCondition = new Normal;
                   delete pCondition;
                   break;
          case 2:  pCondition = new FireAlarm;
                   delete pCondition;
                   break;
          default: pCondition = new Error;
                   delete pCondition;
                   okay = 0;
                   break;
       }
//close switch
    }
//close while true
return 0;
}

Output:
(0)Quit (1)Normal (2)Fire: 1
Logging normal conditions...
(0)Quit (1)Normal (2)Fire: 2
General Alarm log
Warning!
Logging fire call.
(0)Quit (1)Normal (2)Fire: 0


Calling virtual member functions from a constructor can cause confusing results if you are not mindful of the order of construction of objects. When the FireAlarm object is created, by inheritance the order of construction is: Condition, Alarm, FireAlarm.  The Alarm constructor calls Log(), but it is Alarm's Log() function that is invoked rather than FireAlarm's Log() function, despite Log() being declared virtual.  This is because at the time Alarm's constructor runs there is no FireAlarm object.  Later, when FireAlarm is finally constructed, its constructor calls Log() again and this time FireAlarm::Log() is called instead of the base class's Log().




Example #2: Design an Email Manager

 You set out to design a Program to manage e-mail from multiple clients.  Remember the old adage, measure twice, but cut once.  The prevailing philosophy here is, divide and conquer.  After gathering extensive information and through multiple interviews, you decide to divide this into sub-projects.  So far,  you have decided to split the program into these specific modules (you will link them together later):

1 - Communications
2 - Database
3 - E-mail
4 - Editing
5 - Platform issues
6 - Extensibility
7 - Organization and Scheduling

We further divide the implementation into:
1 - Communication
2 - Message Format
3 - Message Editors

Focus:  Focus on the message format first.  There is no point in presenting information to the user until you know what information you are dealing with. We will convert each e-mail from its original format, to the MegaMail 1.0 format, so that only one record format will have to be handled and reading and writing to the disk will be simplified. 

Process: Avoid being concerned with implementation.  Focus on designing a clean interface between classes and delineating what data and methods each class will need.  Design initial classes, stubbing functions out where you need to.  Design around base classes, ADT's and derived classes.  Nouns that relate to the subject, in reality, make good candidates for object names.  Their attribute (the has-a relationship), will become the data members.  Verbs that relate to the subject, in reality, make good function and method names. 

Inheritance: Through inheritance, each derived class should acquire all the data and functions of its base class while having one discrete, additional capability.  You may wish to purchase libraries, rather than creating your own, to save yourself a lot of time.  Don't forget to use the STL when you can. 

Prototype: First, code a working, stripped down prototype.  Use the 80/80 rule.  You can't please all of the people all of the time, but you can try to code for what 80% of the people want 80% of the time.  Use API's if applicable.  The most frequent cause of software dying before its release is that there was not sufficient agreement about what was being built.  Before you start anything, GET THE INFORMATION YOU NEED, get it in writing, get it nailed down into something concrete.  Make sure you are in agreement with and that you understand the client.

API - Application Programming Interface, a set of routines and documentation for using a service.  Most mail providers will give you an API.  In this way, you can code your program to work with other programs installed in the operating system.

Inheritance Hierarchies:

  • Rooted - Share a common base class, all classes descend from a common root class.  The advantage is that multiple inheritance is often avoided.  The disadvantage is that many times implementation will percolate up into the base class.
  • Non-rooted - Objects do not all share a common base class.  There is more than one inheritance hierarchy. 

The interface for MegaMail 1.0, so far, is:

class MegaMailMessage : public MailMessage
{
      public:
      MegaMailMessage();
      MegaMailMessage(pAddress Sender, pAddress Recipient, pString Subject, pDate creationDate);
     
//Other constructors will go here.  Also copy constructor.
      ~MegaMailMessage();

     
//Accessor methods
      pAddress & GetSender();
      void SetSender(pAddress &);

     
//Overloaded operator methods here, including operator equals and conversion routines to
        //turn MegaMail messages into messages of other formats.


      private:
      pAddress itsSender;
      pAddress itsRecipient;
      pString itsSubject;
      pDate itsCreationDate;
      pDate itsLastModDate;
      pDate itsReceiptDate;
      pDate itsFirstReadDate;
      pDate itsLastReadDate;
};

Driver program - a driver program is a function that exists only to test or demonstrate other functions.  Example:

#include <iostream.h>
#include <string.h>

typedef unsigned long pDate;
enum SERVICE { MegaMail, Interchange, CompuServe, Prodigy, AOL, Internet };

//-------------------------------------------------------------------------------------


class String
{
      public:
     
//Constructors
      String();
      String(const char * const);
      String(const String &);
      ~String();

      //Overloaded operators
      char & operator[](int offset);
      char operator[](int offset) const;
      String operator+(const String &);
      void operator+=(const String&);
      String & operator= (const String &);
     
//Overloaded operator << needs to be a friend class so objects outside the class can access it.
      friend ostream & operator<<(ostream & theStream, String & theString);

     
//Accessors
      int GetLen()const { return itsLen; }
      const char * GetString() const { return itsString; }

     
//Static member data, must be defined outside the class
      static int ConstructorCount;

      private:
      String (int);   
//private constructor
      char * itsString;
      int itsLen;
};


//Initializing static member data
int String::ConstructorCount = 0;

//Overloaded default constructor, creates string of 0 bytes
String::String()
{
        itsString = new char[1];
        itsString[0] = '\0';
        itsLen=0;
        cout << "\tDefault string constructor\n";
        ConstructorCount++;
}


//Overloaded (helper) constructor, used only by class methods for creating a new string of required size. Null filled.
String::String(int len)
{
        itsString = new char[len+1];
        int i;

        for( i = 0; i<=len; i++)
             itsString[1] = '\0';

        itsLen=len;
        cout << "\tString(int) constructor\n";
        ConstructorCount++;
}


//Overloaded constructor, converts a character array to a String

String::String(const char * const cString)
{
        itsLen = strlen(cString);
        itsString = new char[itsLen+1];
        int i;

        for( i = 0; i<itsLen; i++)
             itsString[i] = cString[i];

        itsString[itsLen]='\0';

        cout << "\tString(char*) constructor\n";
        ConstructorCount++;
}


//Copy constructor
String::String (const String & rhs)
{
        itsLen=rhs.GetLen();
        itsString = new char[itsLen+1];
        int i;

        for(i = 0; i<itsLen;i++)
            itsString[i] = rhs[i];

        itsString[itsLen] = '\0';
        cout << "\tString(String&) constructor\n";
        ConstructorCount++;
}


//Destructor, frees allocated memory
String::~String ()
{
        delete [] itsString;
        itsLen = 0;
        cout << "\tString destructor\n";
}


//Overloaded operator =
String & String::operator=(const String & rhs)
{
         if(this == &rhs)
            return *this;

         delete [] itsString;
         itsLen=rhs.GetLen();

         itsString = new char[itsLen+1];

         int i;

         for(i = 0; i<itsLen;i++)
             itsString[i] = rhs[i];

         itsString[itsLen] = '\0';
         return *this;
         cout << "\tString operator=\n";
}


//Overloaded non-constant offset operator, returns a reference to character so it can be changed
char & String::operator[](int offset)
{
     if(offset > itsLen)
         return itsString[itsLen-1];
     else
         return
itsString[offset];
}


//Overloaded constant offset operator for use on const objects (see copy constructor)
char String::operator[](int offset) const
{
     if(offset > itsLen)
         return itsString[itsLen-1];
     else
         return
itsString[offset];
}


//Overloaded operator +, creates a new string by adding a current string to rhs, concatenates
String String::operator+(const String & rhs)
{
       int totalLen = itsLen + rhs.GetLen();

       String temp(totalLen);
       int i,j;

       for(i = 0; i<itsLen; i++)
           temp[i] = itsString[i];

       for(j = 0; j<rhs.GetLen(); j++, i++)
           temp[i] = rhs[j];

       temp[totalLen]='\0';

       return temp;
}


//Overloaded operator +=, changes the current string, returns nothing
void String::operator+=(const String& rhs)
{
     int rhsLen = rhs.GetLen();
     int totalLen = itsLen + rhsLen;

     String temp(totalLen);

     int i,j;

     for(i = 0; i<itsLen; i++)
         temp[i] = itsString[i];

     for(j = 0; j<rhs.GetLen(); j++, i++)
         temp[i] = rhs[i-itsLen];

     temp[totalLen]='\0';

     *this = temp;
}

//Definition of our friend class, overloading the ostream operator <<.  MUST be defined outside class.
ostream & operator<<( ostream & theStream, String & theString)
{
        theStream << theString.GetString();
        return theStream;
}

//-------------------------------------------------------------------------------------

class pAddress
{
      public:
      pAddress(SERVICE theService,
      const String & theAddress,

      const String & theDisplay):
      itsService(theService),
      itsAddressString(theAddress),
      itsDisplayString(theDisplay)
      {}

        //pAddress(String, String);
        //pAddress();
        //pAddress (const pAddress&);

      ~pAddress(){}

     
//Friend function overloads the ostream << ooperator
      friend ostream & operator<<( ostream & theStream, pAddress & theAddress);

     
//Accessor method    
      String & GetDisplayString() { return itsDisplayString; }

      private:
      SERVICE itsService;
      String itsAddressString;
      String itsDisplayString;
};


ostream & operator<<( ostream & theStream, pAddress & theAddress)
{
        theStream << theAddress.GetDisplayString();
        return theStream;
}

//-------------------------------------------------------------------------------------

class MegaMailMessage
{
      public:
     
//MegaMailMessage();
      MegaMailMessage(const pAddress & Sender,
      const pAddress & Recipient,
      const String & Subject,
      const pDate & creationDate);

     ~MegaMailMessage(){}

     void Edit(); 
//invokes editor on this message

     pAddress & GetSender()   { return itsSender; }
     pAddress & GetRecipient()  { return itsRecipient; }

     String & GetSubject()   { return itsSubject; }
       //void SetSender(pAddress& );
       //other member accessors

       //Overloaded operator methods here, including operator equals and conversion routines
       //to turn MegaMail messages into messages of other formats.


     private:
     pAddress itsSender;
     pAddress itsRecipient;

     String itsSubject;

     pDate itsCreationDate;
     pDate itsLastModDate;
     pDate itsReceiptDate;
     pDate itsFirstReadDate;
     pDate itsLastReadDate;
};


//MegaMail constructor
MegaMailMessage::MegaMailMessage(
const pAddress & Sender,
const pAddress & Recipient,
const String & Subject,
const pDate & creationDate):
itsSender(Sender),
itsRecipient(Recipient),
itsSubject(Subject),
itsCreationDate(creationDate),
itsLastModDate(creationDate),
itsFirstReadDate(0),
itsLastReadDate(0)
{  cout << "MegaMail Message created. \n";  }


void MegaMailMessage::Edit()
{  cout << "MegaMailMessage edit function called\n";  }

//-------------------------------------------------------------------------------------

int main()
{
    pAddress Sender(MegaMail, "cgermany@networkingprogramming.com", "Charles Germany");
    pAddress Recipient(MegaMail, "jdoe@aol.com","Jane Doe");
    MegaMailMessage MegaMailMessage(Sender, Recipient, "Can we meet sometime today?", 0);

    cout << "Message review... \n";
    cout << "From:\t\t" << MegaMailMessage.GetSender() << endl;
    cout << "To:\t\t" << MegaMailMessage.GetRecipient() << endl;
    cout << "Subject:\t" << MegaMailMessage.GetSubject() << endl;

    return 0;
}

Output:
MegaMail Message created.
Message review...
From: Charles Germany
To: Jane Doe 
Subject: Can we meet sometime today?


As we start out,  pDate is typedefined to be an unsigned long.  This is just a placeholder, eventually pDate may become an entire class to deal with Date objects.  Nest we add the enumerated constant, SERVICE.  This allows Address objects to keep track of what type they are. Then, once again, we rewrite the interface to and implementation of String.  The String class is used for member variables in all message classes and in classes used by messages (not the same as string.h).  Next, the pAddress class is declared.  It will later be expanded for forwards, replies, etc. Service gets tracked as an enumerated const that is a member variable of each pAddress object.  Then we add the interface to MegaMailMessage class.  Later it may be made part of our inheritance hierarchy.  The Edit() function is stubbed out to indicate where it will be when the class is fully operational.  The edit function may be several classes and call many functions in and of itself, since it will require the features of a full-blown word processor.  The main() function is the driver program.  It Accesses accessor functions and the overloaded operator<< . 

Summary:
In accordance with data hiding, clients of the object should not need to understand the implementation details of how they fulfill their responsibilities.  Procedural languages such as C and Pascal are collections of procedures, whereas object-oriented languages such as C++ and Java are collections of classes.  Remember that key and fundamental difference.  Object-oriented programming focuses on integrated data and functions as discrete units that have BOTH data and functions.  Procedural programming focuses on functions and how they act on data.  C++ provides programmers with the tools necessary to manage projects of great complexity. That is one of its many advantages over procedural languages.


©2004 C. Germany