Friday 17 July 2009

Aspcts of Polymorphism in .NET Parts 1 & 2 - Inheritance

Introduction

.NET Developers come in various shapes and sizes, not only physically, but also in terms of their expertise and experience. The polymorphic nature of the .NET Framework now allows their code to benefit from similar diversity. Sadly, though, it is entirely possible with .NET languages and tools, such as Visual Studio 2008, for developers to build programs and web sites without necessarily needing to know or understand the underlying complexity of what they are doing. I've come across so-called developers whose approach to solving problems has greatly improved their search engine skills as they scour the internet for code samples they can lift to fix the problem they're currently facing. Sadly, though, their attitude towards actually understanding and getting to grips with the problem domain is one of laziness. For this reason developers can work commercially, producing viable code and making extensive use of the vast array of controls and tools at their disposal, yet not fully understand such basic lower level concepts as abstraction, inheritance and polymorphism. To be fair, though, this is quite understandable. After all, much of the documentation available on these topics is far from light reading, and tends to go to much greater depth than is necessary for the average beginner wanting to gain a basic understanding. This blog will therefore provide an easier access into these areas of object oriented (OO) programming techniques.

Part One: Object Orientation - What's all the Fuss?

I first came into .NET languages after years of developing solely in Visual Basic. Newer developers, who have only really got into the programming game since the inception of the .NET Frameworks will no doubt be a little confused by this statement. To explain... Visual Basic existed as a much more simple language prior to VB.NET. A VB developer was largely looked down upon by other members of the programming community (Java, Delphi, C++ etc) because Visual Basic was, rightly, viewed as an inferior language. Why? Because it masked a lot of complexity from the developers using it. Inheritance wasn't even an option. During this period I started learning a little Java and had my first exposure to proper OO programming. At about the same time the first version of the .NET Framework was seeing the light of day, and my movement into C# from Java was therefore very organic. So, what's all the fuss about? Well, Object Oriented programming allows a much more flexible and extensible approach to developing your code than you would otherwise have. Let's explain by means of a real world parallel, as I often find these to be the best aids.

If you drive, you no doubt, unless you're very wealthy (in which case, why are you reading this blog?), own a car built on a production line by one of the world's major car manufacturers. Your car started its life on a production line along with a load of other cars just like yours. Let's say, for example, that you drive a Ford Focus. Although your Focus is identical in shape and size to all other Focuses manufactured at the same time as yours, it isn't necessarily identical in all areas. Ford offers a diverse range of trim levels across the Focus range that affects such items as wheels, tyres, engine size, fuel type, leather or cloth seats etc. However, although you may have requested a certain level of individuality for your car, Ford didn't have to send their designers back to the drawing board in order to build your car. They stuck with the basic design of the Focus and simply changed peripherals. This is a real world instance of what Object Oriented programming techniques allow. A programmer can develop a class that does everything he or she wants it to. However, unless he seals the class, which prevents it from being extended (although Method Extensions in .NET Framework 3.5 can provide a workaround for developers wishing to extend sealed classes) another developer can extend or override aspects of the class so that it fits their own individual needs. This is called polymorphism (poly meaning "many" and "morph" meaning shape). When you stop to think about it you'll soon realise the potential benefits you can reap from this.

Part Two: Inheritance

If we were to draw a parallel to the above example in the programming world, engineers at Ford would have built what's called a base class and named it something like "Focus". This class would define and implement various properties and methods. For example:

public class Focus{
private Engine _engine = new Engine();
private double _currentSpeed;
private int _doorCount = 4;
private Guid _keyCode;
public Focus(Guid keyCode){
//Default constructor
_keyCode = keyCode;
}
public Focus(Guid keyCode, int numDoors) : this(keyCode)
{
_doorCount = numDoors;
}
public void Start(Guid keyCode)
{
if(keyCode == _keyCode)
_engine.Start();
}
public void Accelerate(double initialSpeed, double endSpeed)
{
while(_currentSpeed < endSpeed){
_engine.Throttle();

}
_engine.Idle();
}
public int DoorCount{
get{
return _doorCount;
}
set{
_doorCount = value;
}
}
// ...
// ...
}
Now, let's say they decide to release a limited edition Focus that has a push button start, instead of a traditional key turn. One safety condition is that the clutch has to be depressed before the engine can be started. Several other novel features are added, but for the sake of simplicity this is the one we'll focus on (no pun intended). Since the code for the base class is pretty much as it needs to be, the engineers can use inheritance to extend the existing functionality. They therefore create a new object called "FocusLE" (LE = Limited Edition).

public class FocusLE : Focus{

}
By using the ":" operator the compiler is told that this new class inherits from the "Focus" base class. The new class inherits all of base class' public and protected methods and properties. However, we now want to create a new Start() method that takes an additional parameter:

public class FocusLE : Focus{
public void Start(Guid keyCode, bool clutchDepressed){
if(clutchDepressed)
base.Start(keyCode);
}
}
Because the new class inherits from the "Focus" base class the new Start() method can invoke the Start() method in the base class by use of the base keyword once it has done its check to make sure that the clutch is depressed. However, we don't want to leave the old Start() method publically exposed. So we have to do what's called hiding the base class' method.

public class FocusLE : Focus{
public void Start(Guid keyCode, bool clutchDepressed){
if(clutchDepressed)
base.Start(keyCode);
}
//override the StartMethod in the base class
public new void Start(Guid keyCode){
//Do nothing
}
}
Because the engineers who created the the base "Focus" class didn't anticipate a later revision with a different start mechanism they didn't declare the base class' Start() method using the virtual keyword. That's why we use the new keyword when declaring the hiding method. We could leave it out but the compiler would issue a warning. This hiding Start() method means that trying to start the new "FocusLE" without providing the second parameter will do nothing. If we hadn't hidden this method of the base class in our new class the old Start() method would still have been available and we would have had a potential safety risk.

Had the original engineers looked ahead and declared the original Start method with the virtual keyword we would have been able to override this in the "FocusLE" class.

public class FocusLE : Focus{
public void Start(Guid keyCode, bool clutchDepressed){
if(clutchDepressed)
base.Start(keyCode);
}
//override the StartMethod in the base class
public override void Start(Guid keyCode){
//Do nothing
}
}
Part Three: Abstraction

In part three of this blog I will expand our example to explain the idea of abstraction in object oriented programming. Part Four will look at Interfaces and what they're useful for.

No comments:

Post a Comment