Objected Oriented Programming - Basic Concepts and Terms
By Anthony Hart
  Download C# Source Code

Object-oriented programming (OOP) has been around for quite some time now. Nevertheless, many IT professionals still don't have a very good grasp on basic OOP concepts. Still worse, they don't understand how OOP principles can greatly enhance the efficiency, maintainability, and overall usefulness of their applications.

In this article we will first explore some of the most basic concepts in OOP and then apply them to a fictitious music store scenario (the complete code can be downloaded at the end of this article). Because of its excellent support for OOP, we will be demonstrating these comments in the C# language. Along the way, we will endeavor to demystify some terminology common to OOP in hopes that the reader will feel more comfortable in researching it further.

Classes and Objects
The most fundamental concept in OOP is that of classes and objects (hence the term, "object-oriented"). One of the best ways to understand what classes and objects are is to compare them to the world of architecture.

Before a construction crew begins to build a new edifice, the architect sits down and plans for as many contingencies as possible. He ponders ground slope and soil composition, the properties of the building materials, the aesthetic aspects of the building, and many more factors that determine his design. The first tangible result of this process is a set of blueprints. In the next phases of the process, a construction crew erects the building according to the blueprints that the architect designed.

While we are over-simplifying the construction process quite a bit, the analogy is still instructive. In our example, a building was constructed according to a pre-defined plan describing the final product's attributes. Note that a potentially infinite number of similar buildings can be constructed using the same set of blueprints (e.g., think of housing developments). In the world of OOP, this blueprint is referred to as a class, while the building is analogous to an object.

A term we often hear in relation to classes and objects is "instance" or "instantiation". In fact the first definition of an object that I ever heard was, "An object is an instance of a class." Huh? Actually, like almost anything else in life, it's pretty simple once you look at it from a different angle. We could have used the term "instantiation" in our construction example earlier by stating that the building constructed was an instance of the architect's blueprint. It was the tangible realization of the projected plan. The construction workers instantiated the blueprint. They brought into being what was previously a schema on paper.

That is exactly what an object is: a living, breathing thing (well, nothing is really living or breathing in the computer world) built according to plans defined in a class. Furthermore, as in the construction example, a potentially infinite number of similar objects can be instantiated from the same class. This is one of the ways in which code re-use is achieved - we need only write a chunk of code once and then access the class in order to use it again and again. Consider the following code:

class MyClass{
	//This is the blueprint
	public void SomeMethod(){
		//Some code here
	}
}

public void MyMethod(){
	//This is the instantiation of the class
	MyClass mc = new MyClass();

	//This is using the class's code
	mc.SomeMethod();
}
			
Here we see the instantiation of a class and a call to one of its methods.

Inheritance
Inheritance is yet another concept essential to comprehending OOP and maximizing one's use of it. To understand this idea of inheritance, we need look no farther than the local All-You-Can-Eat buffet. On any Saturday evening the buffet is guaranteed to be packed with portly parents and their open-mouthed offspring. As we observe these eager patrons, we can't help but notice the striking physical similarities shared by parent and child. They share so many of the same outward (and perhaps even inward) traits, in fact, that if one of the heavy whelps happens to get separated from his parents and lost between the dessert bar and the tray of insipid, week-old egg rolls, we would most likely have no problem identifying which parents he belonged to and thereby save his blubbering self from further separation anxiety which could quite possibly scar him emotionally and irreparably.

The point of this belabored example, of course, is that in nature we see that offspring inherit certain traits (some external and obvious, and some not) from their parents. Likewise, in OOP a new class can be designed that inherits from another class. In such a case, we call the "child" class a derived class and the "parent" class a base class (this is not to imply that the parents in our buffet example were base, by the way). By using inheritance we can achieve an even greater level of code re-use because whatever code is contained in the base class is also accessible by the derived class plus whatever additional code is written in the derived class to further customize it.



A good example of the usefulness of inheritance can be seen by considering a Button class. In a Windows application we generally expect to be able to click a Button for some corresponding behavior to occur. Usually a Button is rectangular in shape and often has a three-dimensional appearance. By using inheritance, we could conceivably derive a RoundButton class from the Button base class that would behave the same way as a normal Button would, but would have a round shape instead of a rectangular one. This could be accomplished by simply deriving the RoundButton class, re-writing or overriding the code that dictates the shape of the component, and leaving the rest of the inherited code alone.

Code re-use is one obvious benefit of inheritance. Another benefit, though, is that any functions or other code that expect a Button object as a parameter will also accept a RoundButton object. This relationship between a derived class and a base class is what is referred to in OOP as an "IS A" relationship. In our example, a RoundButton IS A Button and can therefore be treated as one programmatically.

Multiple Inheritance and Interfaces
In nature, at least in the case of vertebrates, offspring are created by two parents. Thus the offspring inherits traits from both its father and its mother. In OOP this can also be accomplished but it is generally not advisable. As a matter of fact, many OO languages don't even allow it. We shall demonstrate this problem by considering two classes. Suppose class A contains the following methods:

Draw()
Render()
Erase()
			
Suppose that class B contains the methods:

Print()
Save()
Erase()
			
If we derive class C from both A and B, it would contain:

Draw()
Render()
Erase()
Print()
Save()
Erase()
			
As we can see, there are two Erase() methods with identical signatures. If we were to instantiate class C and try to call Erase() from it, which Erase() method would be called? Don't know? Neither do I. Therein lies the problem. This ambiguity is why multiple inheritance (as this is called) is discouraged, and in many languages even prohibited.

A way to somewhat circumvent the multiple inheritance limitation and still gain some of its benefits is through the use of interfaces. An interface describes certain attributes that a class implementing the interface must have. Code can then be written that accesses those attributes from a class that implements the interface, regardless of what the class may be.

For example, suppose we had an IBarking interface that contained the method, Bark(). In order to apply the interface to our Dog class we would have to make sure that we wrote a Bark() method to fully implement the interface. This would establish an "IS A" relationship between the Dog class and the IBarking interface. In other words, we could say that Dog "IS A" IBarking, allowing us to use the Dog class wherever an IBarking interface is expected. Most likely, that code would want to access the Bark() method. We could also implement the IBarking interface in a BarkingFrog class and send it to the same method that is expecting to be able to access the Bark() method. This code might look similar to:

public void MakeItBark(IBarking ib){
	ib.Bark();
}

public void MyMethod(){
	Dog d = new Dog();
	BarkingFrog bf = new BarkingFrog();

	MakeItBark(d);
	MakeItBark(bf);
}
			
A pseudo-multiple inheritance effect can be achieved through the use of interfaces because in most languages we can implement as many interfaces as we wish in a class. In addition, the class may be derived from some base class. If so, it would be inheriting everything from the base class as well as implementing the necessary elements from its declared interfaces, thus achieving nearly the same effect as deriving from more than one base class.

Code re-use is not realized by using interfaces, but the desirable effects of code compatibility are. Our Dog class, for example, could very well be derived from a Canine class in addition to implementing the IBarking interface. The BarkingFrog class, meanwhile, might be derived from an Amphibian class. Assuming these things to be the case, consider the following modification to our code:

public void MakeItBark(IBarking ib){
	ib.Bark();
}

public void MakeItChewBone(Canine c){
	c.ChewBone();
}

public void MakeItBreatheWater(Amphibian a){
	a.BreatheWater();
}

public void MyMethod(){
	Dog d = new Dog();
	BarkingFrog bf = new BarkingFrog();

	MakeItBark(d);
	MakeItBark(bf);

	MakeItChewBone(d);

	MakeItBreatheWater(bf);
}
			
As we can see in the code, both the Dog and the BarkingFrog classes can be used with the MakeItBark() method, but the Dog class cannot be used with the MakeItBreatheWater() method and the BarkingFrog cannot be used with the MakeItChewBone() method. The only thing these two classes have in common is their implementation of the IBarking interface.

Polymorphism
Another key concept in OOP is that of polymorphism. "Poly" is, of course, Greek for "many", and "morph" comes from the Greek word for "shape". Polymorphism, in general terms, refers to producing different results with the same method call.

Our discussion above about interfaces can be extended a bit further to explain this concept. In the example shown there, we had a Dog class and a BarkingFrog class that both implemented the IBarking interface. This ensured that both classes would have a Bark() method. Each of these Bark() methods could be implemented slightly differently, of course. Indeed, we wouldn't expect a Dog's bark to be exactly the same as a BarkingFrog's anyway. To illustrate, the Dog class's implementation of Bark() could be:

public string Bark(){
	return "woof woof woof";
}
			
The BarkingFrog class could implement it as:

public string Bark(){
	return "rrrroooOOOOoooonnnkkk";
}
			
Therefore, we could make the same call for each class and obtain a different result. Possible code for this would be:

public void MyMethod(){
	Dog d = new Dog();
	BarkingFrog bf = new BarkingFrog();

	d.Bark(); //Returns "woof woof woof"

	bf.Bark(); //Returns "rrrroooOOOOoooonnnkkk"
}
			
Polymorphism can also be effected by overriding a base class's method in a derived class. For instance, if we derived a Chihuahua class from the Dog class, we could override the inherited Bark() method as shown here:

public override string Bark(){
	return "yip yip yip";
}
			
Instead of the base class's standard mutt exclamation of "woof woof woof" this method would produce the annoying "yip yip yip" so common in the smaller, obnoxious, tightly-wound breeds.

Namespaces
In order to maintain at least a modicum of order in our code, it is often helpful to group similar classes together - or at least classes needed for similar tasks. In many OO languages this is done by using namespaces. Namespaces are basically a hierarchical system for categorizing code very much like biology's taxonomical systems in which an organism is categorized first by its Kingdom, Phylum, Class, Order, Family, Genus, and Species.

Continuing with our Dog/BarkingFrog example, we could have first designed some namespace hierarchies such as:

Animal
	\Mammal
		\Canine
	\Amphibian
			
This would give us namespaces such as Animal, Animal.Amphibian, Animal.Mammal, and Animal.Mammal.Canine. Our Dog class could easily be placed in the Animal.Mammal.Canine namespace and we would naturally expect the BarkingFrog class to be found in the Animal.Amphibian namespace. Each of these namespaces could be compiled into their own separate PE file or they could be compiled into the same one. The advantages and disadvantages of both approaches would depend on the application in question.

In a C# project in VisualStudio.NET (VS.NET), when we insert a new sub-folder in the Solution Explorer window and then add a new class to it, VS.NET automatically assigns the new class to a namespace based on the name of the sub-folder and its parent folders. By default it treats the nested folders as nested namespaces. For example, when I added the Bongo class to the Percussion sub-folder as shown in the image below, VS.NET assigned it to the OOP.Instruments.Percussion namespace.

The default code that VS.NET placed in the new class was:

using System;

namespace OOP.Instruments.Percussion
{
	/// 
	/// Summary description for Bongo.
	/// 
	public class Bongo
	{
		public Bongo()
		{
			//
			// TODO: Add constructor logic here
			//
		}
	}
}
			
Note that while this is the default behavior in VS.NET, you're not forced to keep the namespaces this way. You can simply re-write the namespace line of the code to represent the hierarchy you wish to have. Folders and sub-folders are really just a way of organizing your code in a way that's most meaningful to you (and, of course, your development team). You may or may not find it useful for them to also represent your namespace hierarchies.

The Application
[DISCLAIMER: Some of this could get a little dry. The reader has been warned.]

Now that we've thoroughly confused the reader with various OOP terms and concepts, we shall attempt to confuse him even further by explaining the downloadable application that accompanies this article. The application is very trivial but should demonstrate the concepts we've discussed so far. It is a read-only form that could be used by an employee to view the current inventory in a music store and also sample the sound that a selected instrument in the inventory makes. As I mentioned, the application itself is not earth-shatteringly awe-inspiring, but the code behind the scenes is educational.

With this in mind, it becomes apparent that we'll have to design classes that will represent items in the inventory and a class to contain those items. We'll also have to be able to differentiate between the items' sounds somehow. The downloadable VS.NET solution demonstrates one way to do this using the topics we've discussed.

The solution contains two projects: 1) the application that the user interfaces with, and 2) the class libraries that drive the application. The class library's namespace hierarchy is set up as follows:

OOP
	\Products
	\Instruments
		\Percussion
		\Strings
			
The OOP.Products namespace contains two classes: the ProductBase class and the ProductBaseCollection class. The ProductBase class is an abstract class (meaning that it can't be instantiated directly but must be derived from to be used) that contains properties one would expect to find associated with any generic product: Model, Brand, SerialNumber, etc. This class will serve as a base class for all of our specific products. The ProductBaseCollection class serves as a collection of classes that derive from the ProductBase class.

The OOP.Instruments namespace contains an interface called IPlayable. Any class that implements this interface must implement its two abstract methods: Play() and Play(int). Remember this interface because we'll come back to it in a moment.

Another class in OOP.Instruments is the InstrumentBase class. Like the ProductBase class, it is abstract and so must be derived from in order to be used. It serves to represent musical instruments and derives from the ProductBase class in order to include the properties common to all products. Since musical instruments make noise - or can be played - this class also implements the IPlayable interface (I told you we'd get back to it). It implements the interface's methods as abstract methods thereby forcing any classes derived from it to implement them fully. This allows for polymorphism because each class derived from InstrumentBase can give the Play() and Play(int) methods a unique kind of behavior.

In order to represent the diverse kinds of musical instruments we have in inventory, we break the OOP.Instruments namespace down further into OOP.Instruments.Percussion and OOP.Instruments.Strings. The OOP.Instruments.Percussion namespace contains an abstract Percussion class derived from the InstrumentBase class to include its basic properties and some additional custom properties. In addition to this class the namespace contains a Bongo class and a DrumKit class which both derive from the PercussionBase class and include the necessary implementations of the IPlayable methods. The Bongo class, for example, contains all of the basic InstrumentBase and PercussionBase properties (without having to explicitly write them, but rather by virtue of having derived from the base class).

Similar to the design in OOP.Instruments.Percussion the OOP.Instruments.Strings namespace contains an abstract StringBase class that derives from InstrumentBase. It is then accompanied by other classes that derive from it and implement the IPlayable methods.

After all the inheritance and relationships are set up we end up with the following high-level classes to represent our musical instruments in inventory:

OOP.Instruments.Percussion.Bongo
OOP.Instruments.Percussion.DrumKit
OOP.Instruments.Strings.Violin
OOP.Instruments.Strings.Guitar
OOP.Instruments.Strings.ClassicalGuitar
OOP.Instruments.Strings.ElectricGuitar
			
Now, in order to manage these items we have the Inventory class in the OOP.Inventory namespace. This class contains a ProductBaseCollection object to hold whatever objects we may have that are derived from the ProductBase class. It also contains some miscellaneous methods for use in dealing with the collection.

The first project (OOPTestHarness) contains two classes: frmMain and Globals. The Globals class contains globally accessible data members and methods in including the all-important static (accessible without instantiating the class) Inventory object that will hold the store's products currently in stock.

The frmMain class is the application's entry point and the actual graphical user interface. In frmMain, we call the Init() method when the form is loaded in order to initialize the variables we'll need for the application. During this initialization process we create some inventory items and stuff them into the Global class's static Inventory object. In the real world we would most likely query a database to get the information necessary to create this inventory items, but for the sake of simplicity in this example we're hard-coding the information. This part of the process is shown here:

Globals.Inventory.Products.Add(new ClassicalGuitar(WoodType.Cedar, 6, "SomeBrand", "La Dulce", "14A93J45E",
                                                                            Color.RosyBrown, Color.RosyBrown, 750.99));
Globals.Inventory.Products.Add(new ClassicalGuitar(WoodType.Spruce, 6, "SomeBrand", "El Tigre", "98Q23D81H", 
                                                                            Color.Wheat, Color.Wheat, 2299.99));

Globals.Inventory.Products.Add(new ElectricGuitar(WoodType.Ash, 6, "Fender", "Lone Star Strat", "83J29S75F", 
                                                                            Color.OrangeRed, Color.LightYellow, Color.Ivory, 1399.99));
Globals.Inventory.Products.Add(new ElectricGuitar(WoodType.Spruce, 6, "Gibson", "Les Paul", "123K4567G",
                                                                             Color.Black, Color.Black, Color.Black, 1999.99));
Globals.Inventory.Products.Add(new ElectricGuitar(WoodType.Spruce, 12, "SomeBrand", "Duodecimizer", "33Y87A83C",
                                                                              Color.White, Color.White, Color.White, 900.99));

Globals.Inventory.Products.Add(new Violin("PoorBoy", "Backwoods Fiddle", "3902E302E", Color.RosyBrown, 675.99));

Globals.Inventory.Products.Add(new Bongo(2, "SomeBrand", "Bongomaster", "8D63JHD87", Color.SaddleBrown, 100.99));
Globals.Inventory.Products.Add(new Bongo(2, "SomeBrand", "Bongomaster", "2K475H3D9", Color.SaddleBrown, 100.99));
Globals.Inventory.Products.Add(new Bongo(2, "SomeBrand", "Bongomaster", "5SD6F465D", Color.White, 100.99));
Globals.Inventory.Products.Add(new Bongo(2, "SomeBrand", "Bongomaster", "54AG65Z87", Color.Maroon, 100.99));

Globals.Inventory.Products.Add(new DrumKit(8, "Pearl", "Smashomatic", "554J2C382", Color.White, 1500.99));
			
With the application running, we can click the "Show Inventory" button to (surprise, surprise) show the items contained in the global Inventory object. Selecting a displayed item from the will result in displaying its details in the box on the right side of the form. With the details displayed we can see polymorphism at work by clicking the "Play" button for the specified item. You'll notice that the results will be different for different items. Amazingly enough, these different results are produced by executing the same line of code:

string sSound = "[None]";

if(Globals.DisplayedProduct!=null){
	ProductBase pb = Globals.DisplayedProduct;
	if(pb is InstrumentBase){
		sSound = ((InstrumentBase)pb).Play();
	}
	else{
		sSound = "[This product is not an instrument and therefore has no sound]";
	}
}

MessageBox.Show(sSound, "Instrument Sound", MessageBoxButtons.OK);
			
Notice that all we're doing here is making sure we have an item to work with and then calling its Play() method. The rest of the work is taken care of for us in the various classes that derived from ProductBase and implemented IPlayable - the magic of polymorphism and code re-use.

Conclusion
In this article we have discussed some basic OOP topics such as inheritance, multiple inheritance, interfaces, polymorphism, namespaces, and code re-use. We considered several examples to demonstrate these topics and then brought them all together by looking at a complete, albeit small, application. The reader is encouraged to experiment with the application and OOP by trying some or all of the following (as well as any other experiments you may wish to try):

  1. Add some classes to the class library to represent your own favorite instruments and make sure you use them in frmMain.
  2. Create some classes that derive from ProductBase but are not musical instruments (e.g., sheet music, valve oil, guitar picks, amplifiers, mixers, instructional videos, etc.) then adjust frmMain to accommodate these new items.
  3. Print out the source code, make spit wads out of it, and then use them to pester the pudgy people at the packed buffet. This will most likely teach you nothing about OOP, or even software development in general, but it will undoubtedly test your nerve and your innate sense of social responsibility.

Suggested Further Reading
http://java.sun.com/docs/books/tutorial/java/concepts/
http://www.ipipan.gda.pl/~marek/objects/TOA/oobasics/oobasics.html