1)Never start coding unless you understand the task!
2)Gather requirements first. This means identify theproblem and ask questions about it. Now you kind ofknow what to do.
3)Analyze requirements to make sure that they areconsistent and complete. Now you know thatwhatever you are going to do is doable.
4)Produce technical specification (i.e. express therequirements in technical terms). Now you knowexactly what to do.
5)Think of what steps you need to take solve theproblem and express them verbally to obtain a high-level process description. Now you have a processthat you can code!
6)Start coding.
Problem SolvingProblem Solving
* Split complex task in a series of simple steps.
* Identify entities the problem deals with. Thesewill be your classes.
* Identify what data characterizes your entities.These will be your class properties.
* Identify what actions your entities can perform.These will be your class methods.
* Find repeated actions and generalize them.These will be your utility functions or utilityclasses. You will reuse them.
* Don’t make any assumptions: check user inputand make provisions for worst cases.
Process AnalysisProcess Analysis
Problem: Determine if the email addressis valid.
Requirement analysis: What rules can wedefine to check email addressagainst? What is email addresscomprised of?
max.fomitchev@cse.psu.edu
Local Part @ domain
                     subdomain1.subdomain2.tld
Email Validation TaskEmail Validation Task
Core Rules
Single ‘@’ character
Non-empty Local Part
Non-empty domain
Non-empty subdomain(s)
Non-empty TLD
Local Part must not end with ‘.’
Additional Rules
7) 1 < TLD length < 7
8) Local Part length < 65
9) Domain Length < 256
11) Subdomain(s) must not begin with ‘-’ or end with ‘-’
12) Check for invalid characters: ‘\\’, ‘”’, ‘<‘, ‘>’, ‘)’, ‘(‘, ‘]’, ’[‘, ’;’, ‘,’,‘:’, ‘|’
13) Domain cannot contain two ‘-’ in a row
14) Check TLD against the list of ICANN domains
Email Validation RulesEmail Validation Rules
General
There has to be LocalPart and Domain separated by ‘@’
Must not contain general illegal characters (‘\\’, ‘”’, ‘<‘, ‘>’, ‘)’, ‘(‘, ‘]’, ’[‘, ’;’, ‘,’, ‘:’, ‘|’)
LocalPart
0 < Length < 65
Must not end with ‘.’
Domain
0 < Length < 256
There has to be at least one subdomain and a TLD separated by ‘.’
Must not contain ‘@’
All subdomains must be valid
TLD must be valid
Subdomain
0 < Length < 64
Must not begin or end with ‘-’
TLD
0 < Length < 7
Must not contain ‘-’
OR limit TLD choices to domains from ICANN list.
Optimized Rules Technical SpecOptimized Rules Technical Spec
Design Choice: Buffer AllocationDesign Choice: Buffer Allocation
Since you do not know sizes of stringsyou deal with…
a)Do you make an assumption of maxstring length and allocate fixed-sizestring buffers on stack?+simpler +faster +robust -limited
b)Or do you allocate memory on heap(e.g. using the new operator)?-harder -slower -err.prone +universal
Choice A: Fixed BuffersChoice A: Fixed Buffers
// Our Assumptions
#define MAX_LOCAL_PART65
#define MAX_DOMAIN256
#define MAX_SUBDOMAIN64
#define MAX_TLD7
#define MAX_SUBDOMAINS10
// Buffers
char LocalPart[MAX_LOCAL_PART];
char Domain[MAX_DOMAIN];
char TLD[MAX_TLD];
char Subdomain[MAX_SUBDOMAINS][MAX_SUBDOMAIN];
Top-Level Class: EmailTop-Level Class: Email
Email – this is the one we validate
-Contains Address string (property)
-Contains LocalPart (property)
-Contains Domain (property)
-Can be parsed into LocalPart andDomain pieces (method)
-Can be validated (method)
Email ClassEmail Class
class Email
{
   public:
      Email(char* address);
 
   public:
      String Address;
      EmailLocalPart LocalPart;
      EmailDomain Domain;
   public:
      bool Parse();
      bool IsValid();
};
Other ClassesOther Classes
LocalPart
-Contains UserName string (property)
-Can be validated (method)
Domain
-Contains Name string (property)
-Contains an array of Subdomains (property)
-Contains TLD (property)
-Can be parsed (method)
-Can be validated (method)
Subdomain
-Contains Name string (property)
-Can be validated (method)
TLD (closely similar to Subdomain but somewhat different)
-Contains Name string (property)
-Can be validated (method)
EmailLocalPart ClassEmailLocalPart Class
class EmailLocalPart
{
   public:
      EmailLocalPart();
 
   public:
      String UserName;
   public:
      bool IsValid();
   private:
      char UserNameBuffer[MAX_LOCAL_PART];
};
EmailDomain ClassEmailDomain Class
class EmailDomain
{
   public:
      EmailDomain()
      {
          SubdomainCount = 0;
      }
 
   public:
      String Name;
      EmailSubdomain Subdomain[MAX_SUBDOMAINS];
      int SubdomainCount;
      EmailTld Tld;
   public:
      bool Parse();
      bool IsValid();
    private:
      char DomainBuffer[MAX_DOMAIN];
};
EmailSubdomain ClassEmailSubdomain Class
class EmailSubdomain
{
   public:
      EmailSubdomain();
 
   public:
      String Name;
   public:
      virtual bool IsValid(); // Virtual means that the method
                                        // can be replaced in a derived
                                        // class
   private:
      char NameBuffer[MAX_DOMAIN];
};
EmailTld ClassEmailTld Class
// Subdomain and Tld are identical, except for
// slight differences in validation rules, so to avoid repetition deriveEmailTld class from EmailSubdomain base class
class EmailTld: public EmailSubdomain // EmailTld class is derived from
                                                               // EmailSubdomain class
{
   public:
      EmailTld();
 
/* public:
      String Name; */ // Name is inherited from Subdomain
                               // therefore no need to declare again
   public:
      virtual bool IsValid(); // Inherited, but we are going to
                                        // replace it since TLD validation
                                        // is different
};
Email Checker Solution: ProcessEmail Checker Solution: Process
1)Separate email into LocalPartand Domain
2)Separate Domain intoSubdomain[] array and TLD
3)Apply part validation rules to seeif any of them are violated.
Separate Local Part DomainSeparate Local Part Domain
1)Determine email Length
2)Determine AtPosition: theposition of ‘@’ character in email
3)Copy characters 0…AtPosition toLocalPart
4)Copy charactersAtPosition+1…Length to Domain
Pretend that you have alreadysolved concrete tasks (such ascopying characters, findingcharacter index, etc.) with thehelp of String utility class andproceed to construct yourabstract solution by referring toString and other concreteclasses that you have not yetimplemented.
Proceed from Abstract to ConcreteProceed from Abstract to Concrete
(!) Find repeated tasks that can be generalizedand streamlined, e.g.
a)Determining string length (i.e. return the ‘length’of ‘this string’)
b)Copying a range of characters from one stringinto another (i.e. copy ‘length’ characters from‘this string’ starting at ‘startPosition’ into‘destination’
c)Finding character index (find ‘aChar’ in ‘thisstring’ starting at ‘startPosition’)
The above (a,b,c) deal with character strings. Solet’s pretend that we have a String class thatcan do all of the above.
ImplementationImplementation
// String utility class
class String
{
   public:
      String() // Default constructor (builds uninitialized String)
      {
         Buffer = NULL;
      }
      String(char* buffer) // String encapsulates char-array pointer
      {
         Buffer = buffer;
      }
   public:
      int GetLength();
      int IndexOf(char aChar, int startPosition); // !! Returns -1 when aChar is not found
      void CopyTo(int startPosition, int length, String& destination);
      bool IsValid() // Sanity check
      {
          return Buffer != NULL;
      }
   private:
      char* Buffer;
};
String ClassString Class
String Class Encapsulates char*String Class Encapsulates char*
String class encapsulates char*pointer. Therefore a String mustbe initialized with a valid char*pointer!
char nameBuffer[100];
String name(nameBuffer);
We always start with top-level class.
So let’s pretend that Email class is done and theemail validation works and we can use it,e.g.
Email email(“max@adelphia.net”);
if ( email.IsValid() )
   cout << “Email is valid”;
else
   cout << “Email is not valid”;
Where Do We Start?Where Do We Start?
We start filling in blanks from theconstructor of the top-level class.
In the constructor we can refer toother classes we invented eventhough they are not yetimplemented.
Where Do We Go Next?Where Do We Go Next?
Just store the pointer to emailaddress in internal property:
Email::Email(char* address)
{
  // Here we initialize Email’s internal
  // Address property of type String
   Address = String(address);
}
Constructor Email::Email()Constructor Email::Email()
Now, since we already know what amounts tovalid email we can express Email::IsValid()concisely as follows:
bool Email::IsValid()
{
   // Separate email into LocalPart and Domain
   return
      Parse() &&// Was parsing OK?
      LocalPart.IsValid() &&// LocalPart OK?
      Domain.IsValid();// Domain OK?
}
bool Email::IsValid()bool Email::IsValid()
And we know how to parse EmailAddress into LocalPart and Domain:
bool Email::Parse()
{
   // Find ‘@’ character
   int atPosition = Address.IndexOf(‘@’, 0);
   if ( atPosition == -1 )
      return false; // Shute, ‘@’ not found! Therefore cannot parse.
   // Determine email length
   int length = Address.GetLength();
   // Copy email parts in appropriate buffers
   Address.CopyTo(0, atPosition, LocalPart.UserName);
   Address.CopyTo(atPosition + 1, Length - atPosition - 1,Domain.Name);
   return true;
}
Void Email::Parse()Void Email::Parse()
And we know what rules we need to check to make sure thatLocalPart is valid:
bool EmailLocalPart::IsValid()
{
   // We do not assume anything, so check UserName string
   if ( !UserName.IsValid() )
      return false;
   int length = UserName.GetLength();
   // Rule Check: length between 1 and 64
   if ( length < 0 || length > 64 )
      return false;
   else
      return true;
}
bool EmailLocalPart::IsValid()bool EmailLocalPart::IsValid()
And we know what rules we need to check to make sure that Domain is valid:
bool EmailDomain::IsValid()
{
   // Extract subdomains and TLD
   if ( !Parse() )
      return false;
   // Check subdomains
   for ( int i = 0; i < SubdomainsCount; i++ )
      if ( !Subdomain[i].IsValid() )
         return false;
   // Check TLD
   if ( TLD.IsValid() )
      return true;
   else
      return false;
}
bool EmailDomain::IsValid()bool EmailDomain::IsValid()
And we know what rules we need to check to make sure that Domain is valid:
bool EmailDomain::Parse()
{
   // We do not assume anything, so check UserName string
   if ( !Name.IsValid() )
      return false;
   // Contains at least one ‘.’
   if ( Name.IndexOf(‘.’, 0) == -1 )
      return false;
   // Reset SubdomainCount
   SubdomainCount = 0;
   // Extract subdomains
   int startPosition = 0;
   do
   {
      int nextPosition = Name.IndexOf(‘.’, StartPosition);
      if ( nextPosition != -1 )
         Name.CopyTo(startPosition + 1, nextPosition - startPosition, Subdomain[SubdomainCount++]);
      else
         Name.CopyTo(startPosition + 1, nextPosition - startPosition, Tld)
      startPosition = nextPosition + 1;
   } while ( startPosition != 0 );
   return true;
}
bool EmailDomain::Parse()bool EmailDomain::Parse()
TLD is a domain, since all domain rule checking applieswith an addition of one more TLD-specific rule: TLDcannot contain a ‘-’
// Constructor does nothing special, so we simply call
// the base class constructor
EmailTld::EmailTld() : EmailSubdomain()
{
}
bool EmailTld::IsValid()
{
   return EmailSubdomain::IsValid() &&
      Name.IndexOf(‘-’, 0) == -1;
}
bool EmailTld::IsValid()bool EmailTld::IsValid()
Just keep filling in the blanks and your programwill be done!
In the process you will likely modify your classdefinitions to accommodate yourprogramming needs as you discover newthings.
Class design / programming is an iterativeapproach. Thus changing and going backand forth is inevitable and normal.
And So On and So ForthAnd So On and So Forth