Toying with design patterns in PHP
WordPress sucks when it comes to posting PHP Code. Plain and simple.
And this example uses the WP-Syntax highlighter (which works as I'd have it work). In all cases, these posts used a regular opening PHP tag which was subsequently split by WordPress. If you view the source of this post you will see that there is actually another opening PHP tag that is completely not shown above the phpinfo(); function.
I went ahead and ran my code snippet through my own, home grown highlighter and posted from that. It really sucks that I have to do that. You would think it would be something simple for WordPress to be able to post code without a bunch of hoop jumping. Anyway, it sucks. That is that.
The following example code is a PHP version (with some minor changes) of the strategy pattern Java code from the first chapter of Head First Design Patterns. I am really starting to like that book because of the way it makes you learn, with or without your involvement in it.
<?php
abstract class Duck
{
public $flyBehavior;
public $quackBehavior;
abstract public function display();
public function performFly() {
$this->flyBehavior->fly();
}
public function performQuack() {
$this->quackBehavior->quack();
}
// Added this afterward
public function setFlyBehavior(Duck_Fly_Behavior $fb) {
$this->flyBehavior = $fb;
}
public function setQuackBehavior(Duck_Quack_Behavior $qb) {
$this->quackBehavior = $qb;
}
public function swim() {
echo '<p>All ducks float, even decoys.</p>';
}
}
interface Duck_Fly_Behavior
{
public function fly();
}
class Duck_Fly_Winged implements Duck_Fly_Behavior
{
public function fly() {
echo "<p>I'm flying!</p>";
}
}
class Duck_Fly_Not implements Duck_Fly_Behavior
{
public function fly() {
echo "<p>I can't fly.</p>";
}
}
// Added this behavior
class Duck_Fly_Rocket implements Duck_Fly_Behavior
{
public function fly() {
echo "<p>I 'm flying with a rocket</p>";
}
}
interface Duck_Quack_Behavior
{
public function quack();
}
class Duck_Quack_Loud implements Duck_Quack_Behavior
{
public function quack() {
echo '<p>Quack!</p>';
}
}
class Duck_Quack_Mute implements Duck_Quack_Behavior
{
public function quack() {
echo '<p>__ silence __</p>';
}
}
class Duck_Quack_Squeak implements Duck_Quack_Behavior
{
public function quack() {
echo '<p>Squeak.</p>';
}
}
class Duck_Mallard extends Duck
{
public function __construct() {
$this->flyBehavior = new Duck_Fly_Winged();
$this->quackBehavior = new Duck_Quack_Loud();
}
public function display() {
echo "<p>I'm a real Mallard Duck.</p>";
}
}
// Added a new duck type
class Duck_Model extends Duck
{
public function __construct() {
$this->flyBehavior = new Duck_Fly_Not();
$this->quackBehavior = new Duck_Quack_Loud();
}
public function display() {
echo "<p>I'm a Model Duck.</p>";
}
}
class Duck_Simulator
{
public $mallard;
// Added
public $model;
public function __construct() {
$this->mallard = new Duck_Mallard();
$this->mallard->performQuack();
$this->mallard->performFly();
$this->model = new Duck_Model();
$this->model->performFly();
$this->model->setFlyBehavior(new Duck_Fly_Rocket);
$this->model->performFly();
}
}
$duck = new Duck_Simulator();
//$duck->mallard->display();
//$duck->mallard->swim();
?>
Output:
Quack!
I'm flying!
I can't fly.
I'm flying with a rocket
January 19th, 2010 - 19:38
Hi Robert,
This is very helpful to see and learn from this within a PHP model, since that is what I code in!
In the abstract class Duck you have the following code:
// Added this afterward
public function setFlyBehavior(Duck_Fly_Behavior $fb) {
$this->flyBehavior = $fb;
}
Why do you specify the interface Duck_Fly_Behavior as part of the parameter for this function, preceding $fb? It also seems to work without it.
January 19th, 2010 - 22:36
What you see there is something called dependency injection. It is used to enforce an implementation of a known interface. While the code would work fine without the injection, requiring that the argument be of type Duck_Fly_Behavior ensures that the flyBehavior property will always have a known set of properties and methods as specified by the interface.
In our example the Duck_Fly_Behavior has one method abstracted. That method, the fly(), method, is guaranteed to be in any object that implements it so it will always be safe to say that the $flyBehavior property of any Duck class will always be an object of type Duck_Fly_Behavior and will always have a method named fly() that you can call from it.
Does that make any sense?
January 20th, 2010 - 11:46
Robert,
That makes total sense and is now understood. Very well explained. I will be sure to code in that manner since as you outline in your blogs, following best coding practices really pays off in the end.
Thanks for the explanation!