Thursday, April 19, 2007

A brief look at "Abstract Classes" and how to use them...

What are abstract classes?


An “abstract class” is a class that is created with the intention of never being instantiated by an object. Basically this means that the created class will define what properties, methods, and/or events are contained within the class. The methods and/or events may also contain required arguments, as well as expected return data types.


How do you use abstract classes?


We use “abstract classes” through derived classes. Derived classes are the most common type of method used with abstract classes by beginning software developers. This is because they are based on principals that are already learned, and an abstract class can contain both abstract members and non-abstract members. Since the abstract member’s (methods in particular) don’t have implementation code, you may wonder how the derived class would know what to do. This is where polymorphism would come in; you are defining the method at the time you create the derived class, the only thing the “abstract base class” is requiring is that you match all of it’s arguments at minimum and you must use the same data types for the arguments, and if applicable you must use the same data type for the returned values also.


You should be made aware of another way of getting the same result as an abstract class is through an “interface”. Interfaces slightly differ in two ways:



  • Interfaces members must all be implemented (there is absolutely no implementation code in an Interface), where as an abstract class could hold both abstract methods and methods that can be used by the base class, and

  • a derived class can use as many Interfaces as the developer wishes, where as only one abstract class can be used inherited (there are ways around this by creating derived abstract classes of the base abstract class, and then chaining down the abstract classes until all are included and then creating a final derived class to be used – I.E. abstract 1 into abstract 2 into abstract 3 into abstract 4 into derived class 1; thus creating a derived class containing all 4 abstracts).

All of this may seem difficult to understand at first, especially the ‘why would you want to use abstract classes over interfaces’, this will be discussed in a future blog. For now, I’ll concentrate on solidifying the concept and use of an abstract class.


First, I’ll try to give you a better real-world relation to what an abstract class is. I will use an example of abstract art to relate this (you may have heard of this term previously, although not in programming classes or discussions, and you may have even seen this type of art before). Imagine a professor of an advanced art class sets an apple on the table and tells his class he wants an abstract of the apple, and he specifies they must use their own method of creating this art; they are not allowed to use any methods taught within that particular art class. One artist (we’ll call him Artist A) creates an painting of the art using oil-based paints, another artist (Artist B) creates a clay model of the apple, and yet another artist (Artist C) creates a photograph of the apple. Now imagine the same professor in a beginner’s art class and shows them a red rubber ball; he tells them they can only recreate the art using a pencil and paper. So each artist returns with a very basic black, gray and white drawing of the ball.


The reason I use this example is because the professor is a real-world example of an abstract class; he had given a definition to his advanced class of what he wanted (recreation of the apple), and he specified they were to create their own methods. The method was chosen by each artist (painting, pottery, and photography). In his beginner’s class he had specified 1 method that must be used (to recreate the ball). All of these artists would be considered our derived classes. The professor would be an “abstract base class” because he had forced 1 class to create their own method; but also remember that he also had the ability to force 1 class to use a specific method! (NOTE: This will become an important point in understanding differences between “abstract” and “interfaces”)


Now, I’ll give you a real-world programming example of how you would use an abstract class. Imagine you are creating an application that records the results of all examinations performed by any doctor on any patient. We would want to use an abstract class in the records class because we do not have the specific information as to what the exam is, who the patient is, what the results of the exam are, or the comments left by the doctor. In particular our record will need to hold all this information. We obviously don’t want to create a very large block of code to handle all possibilities. First problem we will face is that we don’t know what exams there are or if there are any special requirements of information (from the doctor or the patient) to complete this exam. An example of special requirements is that a patient who has a physical would need to have their age, height, weight and such recorded; where a patient having a follow-up visit for a broken arm may only need their age recorded. Another example would be a patient receiving a pregnancy exam may need to have their expected due date recorded.


A good solution to this is to create an abstract base class that would hold common information for all patients, regardless of exam being used. Since this class is simply just a template of the patients information we need to record we will not want to allow any implementations of the information for two specific reasons, 1) we don’t want to create duplication of information between classes (this allows for easier code maintainability later), and 2) we want to force our derived classes to determine how they want to use the information (we only want to ensure they include this basic information, they can determine if additional information is required).


So, we know that every patient has the property of: first name, last name, and age. We also know that every patient will have a method of: exam results, and doctor’s comments. We would then create the base class using they keyword abstract. We will define any properties and methods as public within this class that the derived class can use; remember we are not telling the derived class how to implement the public properties and methods, we are only telling the derived class that it must implement them (we don’t care how).


You will see in this block of coding I had created a class called “base_patient”. This is our abstract base class:





using System;
using System.Collections.Generic;
using System.Text;

namespace AbstractClass_1
{
public abstract class base_patient
{
//Declare our variables to be used throughout the class
//these are private because we don't want a class or object
//from the outside setting these.
private string firstName;
private string lastName;
private int age;

//We create a public constructor that the derived class
//will use to implement the base class
public base_patient(string first, string last, int ptntAge)
{
firstName = first;
lastName = last;
age = ptntAge;
}

//destructor of object class
~base_patient() { }

//public string to only get the patient's first name
public string frstName
{
get
{
return firstName;
}
}

//public string to only get the patient's last name
public string lstName
{
get
{
return lastName;
}
}

//public integer to only get the patient's age
public int ptAge
{
get
{
return age;
}
}

//Now we define our methods; remember we do not
//create any coding to implement the method; that
//is the job of the derived class.

//public method of string type for exam results
public abstract void ExamResults(string exName, string exRslt);

//public method of string type for doctor's comments
public abstract void DoctorsComments(string dComments);

}//ends public abstract class
}//ends namespace
Listing 1 (“base_patient.cs”)


Now that we have our abstract class, let’s look at an example of where a derived class will use the abstract class. Let’s implement a derived class that is just for a general check-up for all patients; in particular it  will consist of: a blood pressure test, exam results and tonsil examination (if patient still has tonsils). Notice the last examination is based on if the patient still has their tonsils; but, you may have also noticed that our abstract class doesn’t contain this information! Remember, we are using a derived class, so we can create a new property in this derived class to hold that information, and since we are implementing our own method, we can determine if we want anything specific to happen if the patient does or doesn’t have tonsils.


Here is the coding to create the general check-up class, named, “general_checkup”:





using System;
using System.Collections.Generic;
using System.Text;

namespace AbstractClass_1
{
class general_checkup : base_patient
{
//declare variable to be used from within the vlass
//private keyword ensures outside classes cannot use
//the variable.
private int weight;

//Create constructor of derived class; it is important
//that our construct has equal or more arguments as the
//base class; it is also important that our constructor
//matches the order of arguments as the base class.
public general_checkup(string first, string last, int age, int patWeight)
: base(first, last, age)
{
weight = patWeight;
}

//Create our destuctor of the class
~general_checkup() { }

//Create public property of the patients weight
public int ptWt
{
get
{
return weight;
}
set
{
weight = value;
}
}

//Create our overriding method for the ExamResults
//This allows us to define the implementation code
//for this method and by declaring this method public
//we will allow instantiating objects access to this
//method.
public override void ExamResults(string exName, string exRslt)
{
//string exRes;

//We write the exam name and results to the
//console window.
Console.WriteLine("Exam performed: " + exName);
Console.WriteLine("Exam results are: " + exRslt);
//exRes = "Exam performed: " + exName + ", Exam results are: " + exRslt;
//return exRes;
}

//Create our overriding method for the Doctor's Comments.
public override void DoctorsComments(string dComments)
{
//We write the comments from the doctor to the
//console window.
Console.WriteLine("Doctor's comments: " + dComments);
//resultsDocComments = "Doctor's comments: " + dComments;
//return resultsDocComments;
}

//We create a new method to determine the bloodpressure
//exam results; this will later be used in our ExamResults
//method to be displayed back.
public string BloodPressureExam(string systolic, string diastolic)
{
string bpResults;
bpResults = "The patient's bloodpressure is: " + systolic + "/" + diastolic;
return bpResults;
}

public string TonsilExam(string TonsilExist)
{
string tResult,
tExist;
tExist = TonsilExist;
if (tExist == "y")
{
Console.WriteLine("Please enter evaluation of tonsils:");
tResult = Console.ReadLine();
}
else
tResult = "Tonsil's cannot be evaluated. Exam was aborted.";
return tResult;
}
}//ends class
}//ends namespace
Listing 2 (“general_checkup.cs”)

Now that we have a derived class, we can then use this class within our program to display the information of the patient and the results of the exam. Remember all information is stored within an object instance of the derived class,”general_checkup”, because the base class, “base_patient” is an abstract class and cannot be instantiated.


Here is the coding to create the basic program file called “program.cs”:





using System;
using System.Collections.Generic;
using System.Text;

namespace AbstractClass_1
{
class Program
{
//Declare variables to be used within the program
//we declare them public so they may be altered from
//outside classes and objects.

static void Main(string[] args)
{
string resultsBPExam,
commentsBPExam,
verifyTonsilExist,
resultsTonsilExist,
commentsTonsilExam,
getSystolic,
getDiastolic;

//This coding creates a new object and sets the
//appropriate properties.
general_checkup genCheckUpResults = new general_checkup("John", "Smith", 59, 192);

//This coding will ask to ensure the patient has tonsils.
//Be aware this is not the ideal method to do this, as is
//the same with the implementation code within the method
//being called, ideally some sort of verification would be
//performed prior to allowing the method to be called.
Console.WriteLine("Does the patient still have thier tonsils?");
Console.WriteLine("Please enter 'y' or 'n' for answer.");
verifyTonsilExist = Console.ReadLine();
resultsTonsilExist = genCheckUpResults.TonsilExam(verifyTonsilExist);
//We store any comments about the exam here
//we will use an if statement to generalize a comment
//based on if there patient does / doesn't have tonsils.
if (verifyTonsilExist == "y")
commentsTonsilExam = "This patient has nice tonsils";
else
commentsTonsilExam = "This patient does not have any tonsils!!!";

//This coding will call the blood pressure exam method
//again we would ideally want verification coding prior
//to calling the method.
Console.WriteLine("Please enter the systolic reading:");
getSystolic = Console.ReadLine();
Console.WriteLine("Please enter the diastolic reading:");
getDiastolic = Console.ReadLine();
resultsBPExam = genCheckUpResults.BloodPressureExam(getSystolic, getDiastolic);
//We create a general comment about BP results from
//the doctor.
commentsBPExam = "This patient is alive!";

//Now we will build our record and display the results.
//This coding will display the patients basic information
//by calling the properties that were set; this will verify
//the derived class is storing the inherited properties and
//the new property that was created.

//First we clear the console.
Console.Clear();
//We then create our header for the report; this will
//include the patients information.
Console.WriteLine();
Console.WriteLine("This patient's name is: " + genCheckUpResults.lstName + ", " + genCheckUpResults.frstName + ".");
Console.WriteLine("This patient is " + genCheckUpResults.ptAge + " years old.");
Console.WriteLine("This patient weighs: " + genCheckUpResults.ptWt + " lbs.");
Console.WriteLine();
Console.WriteLine();

//Here we return the results that are stored
//in the application from the exams just entered.
Console.WriteLine("The exam information and results are as follows:");
Console.WriteLine();
//This will return the results of the Tonsil Exam
genCheckUpResults.ExamResults("Tonsil Exam", resultsTonsilExist);
genCheckUpResults.DoctorsComments(commentsTonsilExam);
Console.WriteLine();
//This will return the results of the Blood Pressure Exam
genCheckUpResults.ExamResults("Blodd Pressure Exam", resultsBPExam);
genCheckUpResults.DoctorsComments(commentsBPExam);
Console.WriteLine();

//This coding will pause our application so we may
//read what was written to the console, otherwise
//everything may appear faster than we can read.
Console.WriteLine();
Console.WriteLine("Please press the 'ENTER' key to continue...");
Console.ReadLine();
}
}//ends class
}//ends namespace
Listing 3 (“program.cs”)

As you can see from the above coding examples the abstract class can come in handy for determining what properties, methods (including arguments and return values, if we want), and events will be used in the derived class.


You will want to use abstract classes to allow your programs to maintain flexibility and help to ensure code quality. By specifying within the abstract class what properties and methods are required you can ensure that all derived classes contain this; should any derived class not contain this information then an error can be generated. For this to occur you only need to use the keyword abstract in the method that must be inherited and overridden.


In this particular instance I had only created one (1) derived class; this is just to shorten the length of the blog; as you may remember from the real-world comparison I had used three artists, and each one had implemented their own methods of creating the end-result. This is the exact same idea behind programming derived classes. In the first derived class we had created an additional property of Boolean type that had stored if the patient had their tonsils or not.


One other thing I had not quite covered is that you can derive a class from a derived class and have the second-generation derived class inherit all of the first-generation’s derived properties, methods and events. You can also create abstract classes that are derived from an abstract class.


An example of where you may want to create a ‘second-generation’ abstract class, based on our “base_patient” abstract class is if you had wanted to have a sub-set of classes for pregnancy related exams. You may have two exams for pregnancy women, one to determine what trimester of pregnancy the patient is in, and one for general follow-up exams during the pregnancy.


As you can imagine these two pregnancy classes could have overlapping properties; thus you may want to create an abstract class containing general pregnancy properties and methods. Such properties you may want to contain in the abstract class would be the conception date and patients weight (as you can tell, I had omitted this property from the abstract base class for demonstration reasons in this paragraph). You may also want to define methods within this ‘second-generation’ abstract class, such as: a calculation of due date.


Once you have this ‘second-generation’ abstract class you could then create derived classes that would implement these properties and methods as appropriate for their needs. You will want to test these concepts out on your own, such as take this coding and create an abstract class deriving from the abstract base class, then creating a derived class from the ‘second-generation’ abstract class (an idea would be instead of working with dates, you could have the abstract pregnancy class hold the patients number of conceived days, and then have the method defined to calculate the remaining number of days until expected due date. You could have a derived pregnancy exam implement the calculation using the total_days_within_pregnancy minus conceived days).


One last thing, you may be wondering why I chose to call the base abstract class “base_patient”, as opposed to something like “base_records”. This is to show you how polymorphism kind of plays into the abstract world, although this is not truly polymorphism (for more information about “polymorphism” see my previous blog called, “A brief look at polymorphism and how it is used…”). You can make a fairly decent assumption based on the class name that the original intention of the base class was for creating objects, or in this case derived classes, relating directly to patients. Records are somewhat related to patients; however, they are not a direct link to the patient class. In this instance I am demonstrating how you can take an abstract class and use it in any form of derived class as you wish, as long as you maintain the inheritance rules (for my information about “inheritance” see my previous blog called, “A brief look at inheritance and how to inherit”).


As you can see abstract offers a large amount of flexibility in your programs and should be used when possible or practical. In this example we had simply created a way for you to create a default class for records of exams and made it so you can easily add additional record exam classes as you need them. This has minimized the amount of code required, by eliminating repetitive code such as the patients name and age, and it has also used a method to ensure the proper use of the method by defining the method’s arguments. This will seem like a lot of information to take in at first, after a few trial-and-error runs you will quickly get the hang of how to use abstract classes. If you are still unsure of the benefits of using abstract classes, then try re-creating this code by creating separate classes, then imagine trying to do this with a full-featured application that could have hundreds of properties for the patient, hundreds of methods for exams and thousands of records with different types of information; you will quickly realize the benefits that are gained from this simple concept.


In my next blog I will delve a little deeper into the “abstract” world and start to discuss “abstract methods” and the following blog will be about “interfaces”. These two upcoming blogs will compliment, and build upon, your knowledge of “abstract classes”.


Until then…Happy Coding!

No comments: