Tutorials

OO PHP Part 2: Boring OO Principles

by John Kleijn on Jun 7, 2008 11:20:14 AM

3 Defensive Programming

Defensive programming is ‘hope for the best, plan for the worst’. When you write a component, you don’t just assume that it will be used correctly. You validate the arguments and context and let the component fail when you find something at a miss.

We will build on this example, and provide more insight into defensive programming in the next chapter.

4 Heuristics

There are many “heuristics” in OOD that according to many should be accounted with.

These “lessons to be learned by others’ experience”, can indeed help you to avoid problems. They focus on a specific design decision, and are usually the result of trying to conform to OOD principles. OOD heuristics are not at all free of debate though.

Often OOD heuristics predefines the solution to a problem before you really encounter it. Therefore I like to think of them as ‘rules of thumb’: in most cases they hold true.

My advice: adopt these guidelines, but if they limit you in a way you can not compensate for, start looking for alternative solutions.

There are many more published and more descriptive heuristics, but I’m unable (and unwilling) to list all of them.

4.1 Avoid global data

Also known as ‘All global data is evil’. It pains me when I see people do stuff like this:

Do you have any idea what’s going on here? Neither do I. Not only is it unclear what is in those globals, it is also impossible to back trace the execution path by looking at the code. This makes it impossible to debug, unless you wrote it yourself and have a excellent memory. The latter definitely excludes myself.

But there is another type of ‘global data’: statically available data. Consider the following example:

Although the data is not in the global space, it is globally available, and it is quite easy to litter your application with unclear dependencies on some global data. You cannot make any assumptions about the contents of the data. Any components using this data are coupled to the global environment of the application!

In general, you’re better off using known data, shielded from the rest of the environment and passed only by clearly defined and stateful interfaces.

4.2 All properties should be private

This has its roots in Information Hiding as well as Defensive Programming.

Basically the argument is that you should have total control over your properties values, and it definitely has some merits.

Look back at the Defensive Programming example. If hungry was still public, we could pretend we fed Doggie a chocolate chip cookie, simply by setting hungry to 'I could eat some more.'.

But that’s cheating and nullifies the point of having that validation there. What we do instead is declare hungry private, and provide so called ‘accessor methods’, also known simply as ‘setters and getters’. Declaring all a class’ properties private also forces you to make a conscious choice about the accessibility of an objects internal data.

You’ve seen some getters in the User class. Those allow UserDataMapper read-only access to the User properties.

‘Setters’ allow you to validate the input before setting it on the object. In either type of method you may want to do some other things as well, like delegating to a aggregate object (Again, we will cover this later), or keeping track of the number accesses and printing “Congratulations on being the 1000th customer!”.

So, how do we make doggy adhere to all of this?

Since hungry is actually a property of Animal, we’ll have to go in and edit that first.

This is an example of very strict defensive programming: we do not trust children of Animal to provide a valid $hungry string, in fact, we do not even trust Animal’s own method eat to provide a valid string. This may seem like overkill, but it is in fact very good practice that will prevent bugs. If you look closely, you’ll see that the code above will in fact fail (‘not so much’ needs to start with a capital letter).

Mending Dog to work with the stricter Animal is trivial:

5 In conclusion

While this tutorial covers a lot of ground, you may have noticed that we are really only touching the surface. Much of this will be covered in more detail when we go into Design Patterns.

Look forward to a Class Diagram tutorial and a tutorial introducing the most common Design Patterns in the near future. Every tutorial will occasionally look back to provide frame of reference.

I hope you learned a lot, and provided you didn’t fell asleep, you probably did ;D

Till next time.

Comments

Great. You're moving faster than I would've thought. Personally I'm looking forward to reading part six through eight.

I haven't read this one yet, but when I have I'll try to write what I think about it :)

1. Daniel Egeberg on Jun 7, 2008 12:19:04 PM

Curses! You revealed that I have 8 parts planned! Jinx....

2. John Kleijn on Jun 7, 2008 12:27:09 PM

Haha... now you're obliged to write them all :D

3. Daniel Egeberg on Jun 7, 2008 12:36:21 PM

Okay, so I've just finished reading it. I think it's really good, but I'm wondering, seeing as "All properties should be private", do you think it would be wrong to have protected properties so that children classes may modify it?

Edit: I modified your TOC so the page numbers from your .doc are no longer there.

4. Daniel Egeberg on Jun 7, 2008 12:53:18 PM

Strictly, yes.

Absolute best practice would be to have only private properties. Especially when keeping classes 'open for extension', you risk losing the benefits of defensive programming if you declare properties protected instead of private. If you want to give child classes some special privileges, write a protected method that allows them to execute those privileges.

But, I cheat a little in that respect myself and use protected in some cases as well. In particular when I have a class that doesn't have any accessors yet, I can be lazy and use protected instead of accessors. But being lazy doesn't exactly make good practice..

In all, it isn't cast in stone, just best practice.

5. John Kleijn on Jun 7, 2008 2:58:14 PM

I suppose that makes sense. I'll keep that in mind for future projects I'll be working on.

6. Daniel Egeberg on Jun 7, 2008 3:17:48 PM

Putting defensive programming aside, there's another reason why using accessors, within a hierarchy or even in the class itself is a good idea:

"The reason why I chose to use the get_*() and set_*() methods inside the class even though the properties are accessible as well is because if one later wanted to change how to retrieve or set the data only those functions would have to be updated and not everything using them."

Does that quote look familiar? This is about cohesion and centralizing responsibility as well.

7. John Kleijn on Jun 7, 2008 3:24:33 PM

Indeed it looks familiar :D

8. Daniel Egeberg on Jun 7, 2008 3:33:33 PM

Wow.... This was one of the best things I've ever read on OOP. For some reason, the whole responsibility and coupling and what not thing had never clicked before....

Looking forward to part 3 ;p.

9. Corbin H on Jun 9, 2008 12:48:44 AM

Puff! Yay, catching up with the tutorials, love them! :)

10. allenskd on Jul 5, 2008 8:52:37 PM

Just finished reading all 3 of your articles on OOP and I was taking a second look at this and noticed a spelling error. Thought you might like to know so you can fix it :-)

2.3 Don’t Repeat Yourself (DRY)
Second Code Block
'findHigestPoster' -> 'findHighestPoster'

11. Brandon Frohs on Sep 11, 2008 9:00:19 PM

Brandon,

Actually, that's a typo. Not to say there aren't loads and loads of spelling mistakes and grammatical errors in all of my tutorials. I need to find the time look them over properly, but I'm afraid that won't happen too soon.

Thanks for the feedback though.

If anyone wants to volunteer proofreading my tutorials in say a Language Editor function, let me know. ;)

12. John Kleijn on Sep 12, 2008 4:43:32 AM
Login or register to post a comment.