Coming Soon: Object Orientation
Object Orientation is a new look at object oriented programming in PHP. Here is an article courtesy of Precision for an overview.
| Taking PHP the OO way | ||
|
Object Oriented Programming (OOP).
Leendert Brouwer
.
A concept of OOP is often overlooked, misinterpreted or barely touched by numerous books and articles about our favourite language, yet one that proves to be very powerful when used correctly. The future for applying Object Oriented Programming in PHP looks bright, when we look at the new OO features that PHP 5/Zend Engine 2 is going to bring us. It will make this approach much more convenient. With the right tools, all you need is the knowledge. This article will try to explain what objects really are and how they can be recognized, and it will familiarize you with the so called “3 Pillars” that form the foundation of Object Oriented Programming: Encapsulation, Inheritance and Polymorphism.
.
Object Oriented Programming, as the name implies, is about programming with objects. But what exactly is an object? Actually, let me start by stating what an object is not. An object is definitely not just a class stuffed with a bunch of functionalities. This might sound logical to you, but too many times, when I read code on the net, this is what I see. It is easy to make that mistake too: when you discover OO functionality in a procedural language such as PHP, sooner or later the temptation to use it becomes too strong, and you give it a shot without even knowing the first thing about the underlying theory. This is all very nice, but it is not what Object Oriented Programming is about. The textbox defines what an object is:
.
This might sound a little strange, but it really is not. Objects are all around you. Some objects can consist of even more objects. The magazine you are holding is an object. The articles in it are objects. The window in your room is an object. Your room is an object. Everything you can see is an object. But there is a lot more to OOP as a concept. Throughout the years, many methodologies in regard to the object oriented approach have been developed. We can even see three stages in the object oriented development process: Object Oriented Analysis (OOA), Object Oriented Design (OOD) and Object Oriented Programming (OOP), each with their own theory and their own rules. It is a huge concept, and it can take quite some time to fully master this approach, if that is even possible. For example, design patterns (they are basically proven solutions to standard design problems) are still progressing heavily. The end is certainly not in sight yet, and that makes it even more fascinating. It is by far not only a matter of syntax; it is a way of thinking, a way of practicing and a way of applying. Do not let these facts discourage you though – you can come a long way by understanding the basic principles of the OO methodology. There are many advantages to the object oriented approach, of which reusability, extendibility and maintainability are the most important ones. I will explain why these advantages exist when going the OO way:
Misconceptions about applying OOP in PHP
Although opinions differ, I believe that these supposed limitations in PHP’s OOP support are easily compensated, and are not a valid reason to discount using OOP style in PHP programming. Recognizing objects
At this point, it is important to realize that the properties can be objects by themselves, with their own properties and behaviours. In OOP terminology, this is called composition. You could say that a car is ‘composed’ by putting together objects that a car must have. You could even take this further, and conclude that these objects are composed of other objects, and so on. When developing however, make sure that you stay focused when overanalyzing things and thus going outside the scope of your project; it is better to implement only what you need. Now let’s take a look at some things a car can do.
These sorts of things determine the behaviour of a car. They are specific to the car itself. You must be able to recognize objects in real life to be able to create a good OO architecture. It is not a silly thought to practice “thinking in objects” in real life. Look around you. The whole world can be seen as if it was made out of objects. Try to recognize objects around you; it will start to make sense when you do. Take your personal computer, for example. It is composed of many pieces of hardware, each with their own properties and behaviour. Your clothing closet – most likely composed with a number of drawers; and so on. Pillar 1: Encapsulation php $specificCar = new Car(); Wrong. Our rule was that the car should only be either red, green or blue. However, we manipulated the object’s colour property directly. It did not have an interface to take care of its responsibilities. You will find a better example in listing 1. Listing 1 php function setColor($color) $specificCar = new Car(); $specificCar->setColor(“red”); //would alter the object Try to understand this code, and you will see why this is the better way. This time, our car has an interface through which we can modify its properties. The method (functions inside a class are called methods) setColor($color) takes care of what can and cannot happen to the car’s properties. Given that the interface is programmed correctly, we know that nothing bad will happen to its properties, as in the preceding example, when assigning the wrong value to them. The interface will simply take care of that. The method setColor($color) is called a modifier. It modifies a property of the car object. Next to modifiers, we also have accessors. An accessor is used to return a property, instead of modifying it. Listing 2 shows an example of an accessor. Listing 2 php /* // Accessor In listing 2, the method getColor() is called an accessor. Accessors can also manipulate data before returning it. It is good practice to always use accessors instead of referring directly to the object’s internal properties. This way, you can maintain a clear responsibility layer. Now, I just know that some of you are going to say: “Won’t I still be able to manipulate an object’s properties directly anyway?”. This is correct. At the time of this writing, PHP has had no concept of access modifiers like private in for example Java or C++. This will change in PHP 5 however, where the private access modifier will be introduced, thus you can force restricted responsibility without having to worry about your private parts being manipulated. It is important that the behaviour of your object is clearly defined through the interface (or: methods). Just good abstraction does not make for reusability. A well-written interface is also very important when the object is going to be (re)used. Only by looking at the interface, it should be clear what the object can do. Always try coding by using self-explanatory names for your classes, methods and variables. The name should briefly but clearly represent what it does. Do not be afraid to write a few extra characters if you think that would improve the readability of your interface.
Pillar 2: Inheritance
Listing 3 illustrates this theory by implementing a (rather simplified) class that represents a human being. Listing 3 php /* function walk($from, $to) function think($subject) function waveArms() In OOP terminology, this would be called the baseclass for a human being. Let’s pretend that it implements all the required properties and behaviour a human being should have. See it as a universal human being. Of course we cannot implement a complete human being in code (if you can, please raise your hand), but again, this is just by means of an example. We do know however, that man and woman are both human beings, thus, they can both derive from a human being object – they just need respective properties and behaviours added. This also means that if we want to implement a woman or man in code, we’d not have to implement all code necessary to indicate that this is a woman or man. We simply derive it from the human being object. This can save a lot of time and code. And that is also exactly what inheritance is about. To give you a better idea, listing 4 shows an implementation of a Woman class. Note that the superclass definition should always be available to the subclass when you want to extend from it. Listing 4 php /* // constructor // set pregnant to false by default // create modifier for pregnancy attribute // create accessor so see if woman is pregnant The following example shows how to make a new instance of Woman which would also have the properties and behaviour of HumanBeing. When you want to apply this snippet, you have to make sure that the definition of the Woman class is available to the script. Also note that in this case, the arguments passed to the constructor are most likely to be objects themselves, but for the sake of simplicity we have used strings. You can see that we invoke HumanBeing‘s waveArms(), which is possible because we extended Woman from HumanBeing. php $aWoman->waveArms(); $aWoman->setPregnant(true); //easier than you thought, no? if ($aWoman->isPregnant()) { Apart from being able to find a rather interesting alternative to the flowers and bees story this way, there is a lot to say about the preceding code. Firstly, we have implemented all the relevant (note: this means relevant to how we want to apply one) properties and behaviour for a human being. Then, we built upon that class by implementing a class that would represent a woman. In OOP-speak, the relationship between the HumanBeing class and the Woman class is as follows:
We can also still say that Woman “is-a” HumanBeing. Note that it does not work both ways. A HumanBeing is absolutely not per definition a Woman, so we could say that the “is-a” concept is only recognized when we go from the bottom to the top of the hierarchy. A superclass cannot be seen as one of its subclasses, only the other way around. A subclass inherits all properties and behaviour from its superclass by extending from it. This is often called “programming by difference”. All you basically do when subclassing, is adding properties and behaviour that make the subclass different from its superclass. When deriving a subclass from a superclass however, it should be very clear that the subclass is in fact a derivative from its superclass. This sounds obvious, but it is often mistaken. Do not try to replace composition by inheritance, they are two different things. When your subclass cannot be seen as being its superclass, do not apply inheritance. Now that you have seen a few obvious real world examples turning into objects, I think you enough understanding of what objects are to move on to some more webdevelopment oriented situations. Say, you want to create a formgenerator. A form obviously consists of form elements. Now, how would you go around turning your form elements into objects? First, we want to create a baseclass that encapsulates everything that any form element has in common. In listing 5, the class FormElement represents what all form elements have in common. I added a label property to make sure that a form element can have a visible label in a html form, otherwise we would end up with a form with form elements, not knowing what each element is by just viewing the html document. Listing 5 php function FormElement($name, $label) // possibly define accessors, modifiers and other methods here class TextFormElement extends FormElement function TextFormElement($name, $label, $value, $maxLength) $this->value = $value; class CheckboxFormElement extends FormElement function CheckboxFormElement($name, $label, $value, $checked) $this->value = $value; It should be obvious what we did in listing 5. We extended the FormElement baseclass and created two subclasses: TextFormElement, which represents an HTML textfield, and CheckboxFormElement, which represents an HTML checkbox. The derived classes just add functionality to the functionality they already inherit from their superclass, thus specializing themselves. In the following section, we will continue on the preceding examples and make them behave generically. Pillar 3: Polymorphism Listing 6 php function displayStr() class DerivedOne extends Base // overriding displayStr() in Base class DerivedTwo extends Base // overriding displayStr() in Base $objects = array ( foreach ($objects as $object) { What we have done in listing 6 is simple. We define a method displayStr() in a baseclass, and derive two classes from it, namely DerivedOne and DerivedTwo. We then define an array and put instances of the baseclass and its two subclasses into it, after which we loop over the array with the stored objects, and call their displayStr() method. This last part is what makes it interesting. We could say that DerivedOne and DerivedTwo both maintain an “is-a” relationship with Base, but as we can see, the displayStr() is what specializes them. In other words, these subclasses from the Base class behave differently while they are both “a Base”. Once again, there is nothing wow-ish about this code, but it is the theory that matters here. When you think a bit further about this concept, you might realize that this technique can be used to create generic code. I did not even keep track of the number of times I was reading code online where people used switch() statements in object oriented code. The usage of switch() in an OO situation is hardly ever needed, and even if it is, you might want to reconsider it. Using switch() to determine what to do with an object can become a maintenance nightmare. If you use switch() to determine of which type an object is, after which you are going to do things with the object in every different case, requires that you update your switch() statement every time a new object is added to your application. We do not want that, do we? In case you do not really know what I am ranting about, the bad example is shown in listing 7. Say, we want to build a stack of the form elements explained in the “inheritance” section, loop through them, and based on what exact form element (type) we run into, we want to print the HTML code. Listing 7 php // instantiate some form elements // make an array of all the elements so we can loop through them foreach ($objects as $object) { The code in listing 7 is dramatic. Wouldn’t it be nicer if we were able to invoke just one method on the object currently in the loop, and if that object just knew what it is supposed to do? Yes, that would be very convenient. And this is where polymorphism comes in very nicely. In listing 8, we have modified our FormElement class and its subclasses. Listing 8 php /* class TextFormElement extends FormElement /* class CheckboxFormElement extends FormElement /* The new implementation for the FormElement syntax is not very exciting, I know. Let’s draw a conclusion about the bigger context, though. We now know that every class that inherits from our FormElement baseclass, automatically inherits the showHTML() method. While this is not very surprising, it becomes more interesting if we override the showHTML() method in every subclass of FormElement. Aha. Now our subclass objects know what to do when their showHTML() method is being called, without us having to worry of what type our subclass object is. Let us now fix the bad switch() code we saw earlier, in listing 9. Listing 9 php // instantiate a textfield and a few checkboxes // make an array of all the elements so we can loop through them foreach ($objects as $object) { Isn’t that much easier to maintain? I thought so. This is a good way to apply polymorphism. However, there is one remark I cannot leave untouched when dealing with this particular code. In the implementation of showHTML() in our FormElement class, I put a warning message inside the method body, which would show up in case a subclass did not override that method. While this works, it is not very elegant. Unfortunately, PHP has no concept of abstract classes (a mechanism that lets you programmatically force implementation rules when subclassing). If it did, we could quite simply force every subclass to implement showHTML(). Make sure to document properly what needs to be done when you want subclasses to override certain methods of their superclass, for yourself or for your team. It can save debugging time. Conclusion |