Showing posts with label abstract. Show all posts
Showing posts with label abstract. Show all posts

Friday, 17 July 2009

Aspects of Polymorphism in .NET Part 3 - Abstract Classes

Part Three: Abstraction

All of us are aware of abstract concepts, although perhaps we aren't aware that we're aware. To explain... all of us know that there are things we can touch, possess, and things that we can't. For example, we all eat food, but we never actually have a food. Food is an abstract concept and we actually eat instances of food - apples, hamburgers, pizzas, carrots, etc.

Abstraction is at the top level of most things we're familiar with on a day to day basis. In our Ford Focus illustration we were dealing with a concrete instance of an abstract concept - vehicle. Although a person may be said to own a vehicle, it's meaningless without specifying the type of vehicle he or she possesses. For most of us this is a car, but for some it might be a plane, a boat, a bike, a helicopter etc. We then further solidify things by becoming more and more specific about things - what make, model, variation of car we have, for example.

In programming terms abstraction allows us to define a very loose model for something without specifying exactly how the implementation will be handled. Abstraction comes in varying degrees in the programming world. There are Interfaces, which are completely abstract (contain absolutlely no implementation code), and abstract classes, which can contain a mixture of abstract methods and implementation code. This will all be explained before long, so don't worry if you don't understand these terms just yet.

We'll start by moving one step up the vehicle abstraction hierarchy from our Focus, to the Car to explain abstract classes. Cars come in all different shapes and sizes, but all working cars share certain characteristics, no matter how new, old, cheap or expensive they are. They all start, stop, move, turn etc. How these characteristics are accomplished though, can be different from one car to the next. For example, one manufacturer may start the car by a simple key turn, another by a push button, yet another by fingerprint recognition. On the other hand, though, some things are common between all cars - they are all driven by an engine, stopped by brakes etc. These rules apply for cars, but not for other types of vehice - sailing boats, for example, are driven by the wind and stopped (if in a hurry) by an anchor.

So, if we were to extend our model code for the Focus we would have to say that our Focus is a concrete implementation of a Car (abstract class). Of course, because we're building our code in the order in which wer're covering the topics in this blog, we'll next be building our abstract "Car" class. In pratice, we would design our classes using a modelling tool such as UML, designing interfaces and abstractions up front - the inverse to the way wer'e doing things in this blog - and code concrete classes on the basis of our abstractions. For further reading on the matter Google "Design Patterns" or "Gang of Four".

There is a lot to be said for abstracting functionailty, but I strongly believe that you need understand the bigger picture first.

Let's have a look at how we code an abstract class in C#:

namespace Ford
{
public abstract class Car
{
public abstract void Start(Guid keyCode);
}
}


This is a very basic start, but it demonstrates all that we need to show for now... First of all notice the use of the keyword abstract. This marks the class as being abstract, so instances of this class cannot be created. So,

Car c = new Car();
will result in a compilation error:
Cannot create an instance of the abstract class or interface 'Ford.Car'
Next we have an abstract method:

public abstract void Start(Guid keyCode);


Notice that this method doesn't have a body (i.e. no curly braces).
That's because the method is abstract - its implementation, or how it will start, must
be coded in a class that implements this abstract class. We'll soon see
how this affects the Focus class, which we'll alter to implement the Car class.


However, abstract classes can contain implementation logic too, which is then inherited by the classes that implement it. For example, since in our fairly basic example, we can safely assume that all cars will accelerate by engaging the engine and stop by engaging the brakes, we can promote this logic to the abstract class level. Then all cars will benefit from this standard logic.
Our modified abstract class now looks like this:
   public abstract class Car
{
protected Engine _engine = new Engine();
private double _currentSpeed;
private Brake[] _brakes = new Brake[4] { new Brake(), new Brake(), new Brake(), new Brake() };

public abstract void Start(Guid keyCode);

public void Accelerate(double initialSpeed, double endSpeed)
{
while (_currentSpeed <>
{
_engine.Throttle();
}
_engine.Idle();
}

public void Brake()
{
_brakes[0].Apply();
_brakes[1].Apply();
_brakes[2].Apply();
_brakes[3].Apply();
}

}

Now we'll look at the changes we need to make to our Focus class in order to implement the Car class.

As a reminder, let's look at the code as it was:


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;
}
}

// ...
// ...

}


In order to implement the new Car abstract class the following changes need to be made:

   
public class Focus : Car
{
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 override void Start(Guid keyCode)
{
if (keyCode == _keyCode)
_engine.Start();
}
public int DoorCount
{
get
{
return _doorCount;
}
set
{
_doorCount = value;
}
}
// ...
// ...
}


Notice the changes to the class:
1) We've added : Car to the class declaration. Just as with the extension of Focus into FocusLE, this notifies the compiler that we are going to be implementing the Car abstract class.
2) The Accelerate method has been removed since this logic is now contained in the abstract class.
3) We still have the Start method, although we've had to add the [italic]override[/italic] keyword to the method statement.

Why do we get rid of Accelerate, but keep Start? Because Start is declared as an abstract method in the Car class. In other words it must be implemented in a child class, such as "Focus". On the other hand, Accelerate contains implementation logic within the abstract "Car" class and therefore doesn't need to be overriden in the "Focus" class - although it [italic]could be[/italic] if needed.

Now, when we create an instance of Focus we get access to the Accelerate and Brake methods. Let's create a new class, called "Driver" and demonstrate this inheritance:

    public class Driver
{
public void Init()
{
Guid _keyCode = new Guid();
Focus myFocus = new Focus(_keyCode);
myFocus.Start(_keyCode);
myFocus.Accelerate(0, 50);
myFocus.Brake();

}
}
The code highlighted blue demonstrates the fact that the "myFocus" object (i.e. the instance of the "Focus" class) can Accelerate and Brake even though "Focus" doesn't define these methods.

Why?

You may be wondering - "why is any of this useful?" Well, now that we've abstracted things out to the level of "Car" we can deal at that abstract level. Say for example, we were creating a class called "Garage". We can now easily build that "Garage" class around Cars rather than Focuses.

    public class Garage
{
public void Service(Car aCar)
{
//...
//Code to Service the care
//...
}
}
Because every "Focus" is also a "Car" (by virtue of the fact that it has implemented the "Car" abstract class) this, and any other class that implements the "Car" abstract class can now be serviced at the "Garage".

Once you understand the principles of abstraction it doesn't take long to realise the massive potential for it to make your code more flexible and extensible.

Summary

This tutorial has demonstrated what abstract classes are, and by the time you've finished reading this series of blogs on polymorphism I hope you will feel sufficiently equipped to go on to further reading, such as books on Design Patterns. These books will help you to get a stronger grasp on the far reaching benefits of abstraction.

The next and final part of this blog series will deal with interfaces.

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.