Object-oriented programming is still a relatively new technology in the world of web development; and, as such, some of the concepts are widely misunderstood by many capable web developers. This can be best illustrated by the recent boom of interest in the Javascript object literal.
As Senior Web Developer at Rentokil Initial, I am required to be technical lead within the team. This also means making sure the technology is understood and used correctly. With this in mind, I’ve recently noticed that many of my team-mates obviously don’t understand the underlying concepts of OOP. For this reason, I’ve prepared this short tutorial on the concepts - to be followed by more detailed posts on execution in Javascript and in PHP.
What is Object-Oriented programming?
Object-oriented programming can best be describe as a programming paradigm; a methodology of programming adopted by a programmer. Another example of a programming paradigm would be procedural (or sometimes, imperative) programming.
Procedural programming involves viewing a computer program as a sequential list of computational steps (or procedures) to be carried out. In procedural programming, the list of procedures can be further broken down into subroutines and functions (not to be confused with mathematical functions which are used within functional programming).
This is very much an “old skool” approach to programming and is adopted by many high-level languages such as BASIC. It’s also worth pointing out that most Web-based languages, such as PHP, ASP and Javascript, can be written using this approach - in fact when I first started out as a web developer, I was using ASP script, PHP and Javascript in this very manner, blissfully unaware of the OOP potential there-in.
In OOP, a program is seen as comprising a collection of individual modules, or objects, that act on each other. Each of these objects could be seen as an independent program in itself, with a distinct role or responsibility.
OOP provides greater flexibility and easier maintainance across large systems and can sometimes make understanding and analysing complex procedures a lot easier.
It’s worth noting, at this point, that most OOP languages (such as C++ or Java) are stateful where as often procedural programming languages are stateless.
Enemy of the State
In computer science, a “state” is a particular set of instructions which will be executed in response to the machine’s input. An information system or protocol that relies upon state is said to be stateful. One that does not is said to be stateless.
OOP PHP is stateless because each script must be called on a page refresh where as Javascript is stateful because it can make use of event listeners - specific input - and the wonders of XMLHttpRequest - thus negating the need for refreshes.
Phew. That was all quite technical and filled with emphasis wasn’t it? OK, so let’s move on…
Objects and Classes
The term “Object,” that gives OOP it’s name, refers to a conceptual object that represents an item in our program or system. This could be anything from an online form or a computer file, to a real world object such as a car.
This representation consists of attributes - the characteristics of our object; and methods - a set of functions and calculations that are either performed to modify the object itself, or are involved in some external effect.
The term “Class” represents the definition (or classification - class) of our object. For example, if we were to write a class called “car”, we could create any number of instances of that class - say “Porsche”, “Ferrari” and “Jaguar”. Each of these instances is an Object. This illustrates that a class is effectively a set of objects that all share common attributes.
Keeping it “in the family”
One of the greatest advantages to using objects is encapsulation. This basically means that data within an object is only available/modifiable via the object’s methods - this is generally known as the interface of the object.
This resultant limitation of scope allows an object-oriented programmer the freedom to declare attributes, variables and methods without having to worry about clashes with those in other objects.
Encapsulation also means that, as long as we don’t alter the interface, we can change how an object works (to increase performance, add functionality etc) without affecting the rest of our system.
Now let’s take a look at how objects and classes can relate to each other.
Note: From this point on, the concepts are going to get a little more complicated. Please bear in mind that I’m writing an introduction here - I fully intend to go over these concepts again (with examples) whilst applying them to Javascript and PHP in the tutorials to follow.
Characteristics of a Class
When writing a class, there are a number of characteristics that are worth taking into account:
Constructor
The constructor of a class is a special operation that is run upon instantiation - when an object is created. They are often distinguished by having the same name as the class itself. It’s main purpose is to set-up the attributes of a class and to establish the class invariant - to basically make sure that the attributes of the class conform to the class interface. It cannot have a return type.
A properly written constructor should never contain any functionality that could fail, thus leaving the object in an invalid state.
Destructor
The Destructor of a class is the opposite of the constructor in that it is run when an object is destroyed. It’s function is to clean up and to free the resources which were used by the object during run-time and unlink it from other objects or resources. This should result in the invalidation of any references in the process.
Relationships
There are a number of relationships that can be used when interaction is needed between objects. These are as follows:
Inheritance
Inheritance allows a (sub)class to inherit all the attributes and methods of a parent class - in effect, extending the parent. It can best be described as an “is a” relationship. For instance, if we had a class of “fruit”, we could extend it by defining classes of “apple”, “orange” and “banana”. Each of these subclasses could be described in the following way:
apple "is a" fruit
orange "is a" fruit
banana "is a" fruit
Because each of our subclasses extends the “fruit” class, it has all the attributes and methods of a “fruit” plus any specific characteristics of it’s own.
Here’s a more web-development-oriented example using form elements and presented in pseudocode:
class formElement
{
Attributes:
id
name
class
}
class input extends formElement
{
Attributes:
value
type
}
class textarea extends formElement
{
Attributes:
cols
rows
}
In this example the two classes “input” and “textarea” have inherited from “formElement”. This means they inherit formElement’s attributes like so:
input is a formElement
input
{
Attributes:
id
name
class
value
type
}
textarea is a formElement
textarea
{
Attributes:
id
name
class
cols
rows
}
However, as the parent (super) class, formElement stays exactly the same:
formElement
{
Attributes:
id
name
class
}
As you can imagine, this relationship can be incredibally useful. Some languages even allow multiple inheritance where a class can have more than one parent (super) class. Sadly this isn’t the case in either Javascript or PHP, however it is possible to obtain the same effect using other types of relationship.
Composition - Association and Aggregation
Composition is a slightly different sort of relationship - this is where it could be said that a class was “composed” of other classes. For instance, a wall is “composed” of bricks and a molecule is “composed” of atoms. Neither of these examples could be described as inheritance - the statement, “a wall is a brick” simply isn’t true. Composition can be described as “has a” and “uses a” relationships; a wall “has a” brick or a wall “uses a” brick.
Let’s take a look at a “has a” relationship in pseudocode. Let’s first define some simple classes:
class brick
{
}
class wall
{
Attributes:
brick1
brick2
brick3
brick4
Methods:
// Constructor
wall()
{
this.brick1 = new brick();
this.brick2 = new brick();
this.brick3 = new brick();
this.brick4 = new brick();
}
}
You can see that “wall” contains a number of brick attributes. We want each of these attributes to be a “brick” object. To do this we simply instantiate them within the constructor of the “wall” class. Each of these brick classes will function as a normal class but also as an attribute of “wall.” This is known as association.
Now let’s take a look at a “uses a” relationship:
class person
{
}
class car
{
Attributes:
driver
Methods:
// Constructor
car(driver)
{
this.driver = driver;
}
}
me = new person();
myMotor = new car(me);
In this example, we can see that the “person” class has been instantiated into the object “me”. This object is then passed into an instantiation of the “car” class. This means that the object “myMotor” (our instantiation of the “car” class) uses the object “me”. This relationship is known as aggregation.
Update: Thanks to Nate Logan for spotting a small error in that last example and emailing me.
The main difference between these two relationships is simple; in association the composing classes are destroyed when the parent is destroyed, however, in aggregation they are not. This ultimately means that, when aggregation is adopted as a relationship, the composing classes and objects can be re-used in other classes and objects within the system.
Polymorphism
An object-oriented programming language must support polymorphism; meaning different classes can have different behaviours for the same attribute or method. This can best be illustrated with an example:
class formElement
{
Attributes:
id
name
class
Methods:
getHtml()
{
// returns generic form element HTML
}
}
class textarea extends formElement
{
Attributes:
cols
rows
Methods:
getHtml()
{
// returns textarea form element HTML
}
}
As you can see in the example, both classes have the method “getHtml” but the “textarea” class is a subclass of the “formElement” class. This results in the “getHtml” method being overloaded. Overloading is a good example of polymorphism in action - an object-oriented language must support polymorphism in order to know which “getHtml” method applies to which object.
Polymorphism, at it’s most basic, describes the fact that a given function may have different specifications, depending on the object to which it is applied.
Why use Object-Oriented programming?
Object-orientation can help keep projects simple by breaking them down in to managable chunks. Those chunks can then be re-used in other projects, thus saving time in the long-run. In fact, adopting an object-oriented approach can be the foundation of a truly successful team environment; through promoting modularity, an object-oriented environment breeds improved code reusability and maintainability.
But don’t just take my word on this; try it for yourself! To better help you to do that, I’m going to follow up this introduction to object-oriented programming with tutorials on it’s use within Javascript and PHP.
So in closing, I hope you’ve learnt something new from this overview. Please feel free to post comments and questions and I’ll endeavour to reply to the best of my ability.
Like this post? Digg it, Del.icio.us it, Ma.gnolia it!