The other day I had to interview a lot of PHP developers for my role as team lead in this ultra cool Belgacom Project. I interviewed many and on their resumes they all looked to be very proficient and have a lot of years of experience. One of the things I was looking for were good PHP developers with an excellent grasp of Object Oriented Programming. All of the developers claimed to be proficient in PHP5 and some even put an OOP expert status. Yet during the interview they failed miserably. Linking my questions about Interfaces and Abstract classes to a Java interview…
When I told my wife about the candidates I send home she asked me why. Here is the excerpt of that conversation.
Chanie: You keep interviewing all those PHP programmers? Aren’t they any
good?
Nick: The problem is a lot of them didn’t evolve with the programming language.
Chanie: What do you mean?
Nick: Well PHP5 is released July 2004. That is almost 7 years ago. If you take a look at the average life cycle of an application that means a looooong time ago. Applications that still run on PHP4 are considered fossils. So how can a developer justify not to learn PHP5 and all it potential and still dare
to say I am proficient in PHP5?
Because upgrading your server to PHP5.* is not the same as using PHP5.
Chanie: If upgrading your server and thus running your software on the latest build isn’t PHP5 programming, then what is?
Nick: In PHP4 it was possible to perform object oriented programming but very limited and certain workarounds needed to be taken. That meant almost nobody used classes, and those that did, most likely did it wrong.
Chanie: what do you mean did it wrong? A class is a class, what is more to it?
Nick: Well you have to know what kind of logic you have to put in a class and how to implement it nicely so you do not simply wrap procedural code within a class statement. That means keeping
classes lean and small, only doing things for what they were designed.
Chanie: What do you mean doing things for what they were designed? You decide what their design is!
Nick: Indeed it is the developers responsibility to think about the class its design before implementing it. This means splitting your logic in several classes which are loosely coupled.
Chanie: What do you mean loosely coupled? I have been writing classes a long time but this is the first time I hear about that.
Nick: Well that is because you aren’t really doing oop.
Where is the reusability and flexibility if everything is strongly coupled? When you try to make classes loosely coupled you try to make the dependency between two classes to a minimum. If class A must be coupled with class B it
would be much nicer if you indicate the dependency using an interface and not an actual implementation.
Chanie: What has an interface got to do with coupling?
Nick: An interface defines a contract listing all the public methods a class must implement so that the Client knows which methods will be available when he uses a class that implements the interface. There is a huge difference between giving a class direct knowledge of one class to another, which is based on implementation, rather than based on a contract. When a system is dependent on an actual implementation it is considered strongly coupled. In loosely coupled the client doesn’t need to know which class is used as long as it implements the interface which means a new class implementing the interface can be written to replace the current dependency, without the need to change the dependent class. New and old classes can be changed interchanged freely. Strong coupling doesn’t allow this.
Chanie: aaah that makes a lot of sense. I heard about interfaces but I thought it was to let the client know which methods were available, and I never thought about the consequences or why it should be used in real life scenarios.
Nick: Well now you know.
Chanie: But this raises the question about Abstract classes, they are also a kind of interface. Because you have to extend them before you can use it. And if you would use the abstract as the
contract it would provide the same purpose.
Nick: Indeed it would, but an abstract class has implementation details which are common between all extending classes.
Chanie: Indeed that way you are also strongly coupling the client to a definite implementation and thus when a certain common method implementation needs to change, you also need to change the client. Now that I Think about it, it would be better to let the abstract class implement the interface and make the dependency towards that.
Nick: Right, also besides implementing common logic in an abstract, it can also be used to enforce a kind of algorithm, without knowing all the details of the algorithm required.
Chanie: Huh, you lost me?
Nick: Imagine you want to implement a web service client.
You could implement everything with the Soapclient class but you could also abstract it. Creating a common interface for all your clients, SOAP, XML-RPC, etc. Then in your abstract web service class you implement
all the common logic in the abstracted methods. Keep all naming abstract, the interface should be abstract such as doRequest() and not something like doSoapRequest().
Chanie: ok but what about that algorithm?
Nick: I’m getting there. So now that you have defined the requirements you know all the potential service clients will have a specific implementation for the doRequest(). So you make that method abstract enforcing the extending classes to implement it accordingly. Now from a client perspective you instantiate the kind of service client you need:
SoapClient or Xml-Rpc and when you make the getLatestNews() method, internally the doRequest() method is called. So both classes share the same algorithm in getLatestNews and the only thing that differs is
the actual implementation doRequest, but this is of no matter because the algorithm doesn’t need to know that exact implementation. It only needs to know it has to call it on that exact moment in time.
Chanie: aaaah now it becomes clear. I only knew about the shared use case for abstract classes. But I didn’t know about hiding the exact algorithm details use case.
Nick: That is because people often do not use it correctly. Most people I know use an abstract class to create a collection of static utility functions into a class and then declare it abstract so nobody can instantiate the abstract class directly.
Chanie: Aaaah yes static functions and properties. I like those, it gives me back the power of scripting. I have a bunch of functions I declare static and put them in a class and voila, everything is nicely packaged.
Nick: Wrong my young padewan. You are going to the dark force. It is a dark but powerful path you follow. But in the end it will lead to your own destruction.
Chanie: explain!
Nick: Static methods are not created to facilitate your packaging needs. If you want to do packaging try namespaces using the conventional PEAR naming convention, Vendor_Package_Date_* or the real namespace deal introduced in PHP5.3, which is already almost 2 years old… Static
methods are created to assist in creating instances of a specific class. A class with a private constructor can never be instantiated and is thus useless, unless it can be instantiated using a public static method. The
only purpose of this method is to check whether an instance already exist and if not create it and store it in a static property, otherwise return it immediately. This is called a singleton and will make certain
only 1 occurrence or instance can exist from a certain class. Static methods are also used to create and configure instances from classes which use a common interface. Thus you do not instantiate a concrete
class yourself but let a factory do this for you. So your client never knows which class is actually instantiated but it is decided through the means of a configuration detail from inside or outside the factory
itself. You now see the power of interfaces? It promotes polymorphism.
Chanie: Polymorphism isn’t that some kind of alien probing creature??
Nick: lol, no it isn’t. It is the ability to let the client work with various types. An interface can be implemented in several ways but those implementations can be interchanged freely. Even dynamically if you want.
Chanie: Dynamically, but isn’t it only possible during instantiation?
I have created a small powerful game… And demonstrates what you call polymorphism. In this game I am girl who can attack zombies. I have 2 public methods: look and attack. In the beginning when I attack I use my fists. But after taking a powerup I will be able to shoot fireballs.
$player = Nbe_GirlVsZombie_Player_Girl;
$player->attack();
$player = Nbe_GirlVsZombie_Player_GirlWithDragonPunch;
$player->attack();
Nick: Yes it demonstrates what I call polymorphism. Both Players have a different implementation and yet can be freely swapped because the interface is your contract. But it is not really dynamic, you still have to really instantiate different types and what if you want your player
to all of the sudden have night vision? You would have to create another player type. It would grow exponentially and become a nightmare to handle… Better to do that dynamically using a strategy, injecting the behavior you need at the fly.
Chanie: oooh that is sooo cool.
Nick: isn’t it? Well I would get me a girl instance using a factory:
$player = Nbe_GirlVsZombie_Player::factory();
$player->setAttack(Nbe_GirlVsZombie_Player::factory(‘fist’));
$player->attack();
$player->setAttack(Nbe_GirlVsZombie_Attack::factory(‘dragonPunch’));
$player->attack();
$player->attack();
$player->setVision(Nbe_GirlVsZombie_Vision::factory(‘night’));
$player->look();
Nick: In this example you see polymorphism, static methods and interfaces being used. You see how Object oriented programming is so much more than just a class and some code in it. You need to really think and put logic where it is needs to be put. You do not want to put the code of an attack in the player its class because that would be putting the logic in the wrong place. The attack function should
delegate the attack to attack instance but not through a strongly coupled design decision but through a loosely coupled design by using an Nbe_GirlVsZombie_Attack_Interface which every attack must implement.
Nick: I hope this is all become a little bit clear.
Chanie: ooh yes it has become clear I still have a lot to learn, but thanks to this conversation I have a better understanding and I am eager to put it in practice.
Nick: glad to hear that! Have fun with OOP!
Well that was my complete conversation. Of course a potential team member has to have a lot more than good OOP skills, he or she will have to have the right social skills and mentality. OOP you can learn, but
behavior skills are a little harder to tackle…
Sunny Greetings,
Nick Belhomme