Code monkey like design patterns lots

I was thinking today that though I carry the title of Web Developer around with me wherever I go I have not been developing for the web much the last few months. Come to think of it, I cannot say that I have developed anything for the web in about five months or so.

Don’t get me wrong. I have been coding PHP like a mad penguin with 20 hands and an incredible itch at the tip of his fingers. Its just that the code I have been writing has been either framework code that drives web apps or CLI code that will be used for integration projects from the command line or cron.

And I absolutely love what I am doing. I have been employing design patters almost as though they came straight from a book. Without even knowing I was doing that.

And it made perfect sense to do it. Little modular classes that are built of maybe two methods and a property each that totally allow for expansion later just by adding the controller and the model. You just gotta love it.

And because I have had the opportunity to do so, I have been using vi(m) quite a bit more than I have in the past. In fact, today I used nothing else. And I was totally thrilled to use it.

I felt faster and way more in control without having to touch my mouse. And I was able to code, shell out, process SVN commits, file searches, CLI stuff… everything I needed to do without ever leaving my editor and without ever touching my mouse. How awesome is that?

I think my zeal for the type of coding is that I felt like a programmer today. Not a script kiddie, not a PHP n00b, but a programmer. A guy who writes code then implements it and instantiates it at the CLI to watch (you ready for this?) absolutely nothing come to the screen – because that is how we want it to work.

Yeah, I love being a geek. And it doesn’t take much to make me happy as a geek. A sweet little editor, a freaking rad OS and a great programming language like PHP. Oh yes, color me happy. And color me geek.

Chapter 4: The factory pattern

Again, a little later than I would have liked, but here is the translation of the first run factory pattern from the Head First Design Patterns book:

<?php
/*
class SimplePizzaFactory {
	public function createPizza($type) {
		$pizza = null;
 
		if ($type == 'cheese') {
			$pizza = new CheesePizza();
		} elseif ($type == 'pepperoni') {
			$pizza = new PepperoniPizza();
		} elseif ($type == 'clam') {
			$pizza = new ClamPizza();
		} elseif ($type == 'veggie') {
			$pizza = new VeggiePizza();
		}
 
		return $pizza;
	}
}
*/
abstract class PizzaStore {
	//public $factory;
 
	//public function __construct(SimplePizzaFactory $factory) {
	public function __construct() {
		//$this->factory = $factory;
	}
 
	public function orderPizza($type) {
		$pizza = $this->createPizza($type);
		$pizza->prepare();
		$pizza->bake();
		$pizza->cut();
		$pizza->box();
		return $pizza;
	}
 
	abstract protected function createPizza($type);
}
 
class NYPizzaStore extends PizzaStore {
	public function createPizza($item) {
		if ($item == 'cheese') {
			return new NYStyleCheesePizza();
		} elseif ($item == 'pepperoni') {
			return new NYStylePepperoniPizza();
		} elseif ($item == 'clam') {
			return new NYStyleClamPizza();
		} elseif ($item == 'veggie') {
			return new NYStyleVeggiePizza();
		} else {
			return null;
		}
	}
}
 
class CAPizzaStore extends PizzaStore {
	public function createPizza($item) {
		if ($item == 'cheese') {
			return new CAStyleCheesePizza();
		} elseif ($item == 'pepperoni') {
			return new CAStylePepperoniPizza();
		} elseif ($item == 'clam') {
			return new CAStyleClamPizza();
		} elseif ($item == 'veggie') {
			return new CAStyleVeggiePizza();
		} else {
			return null;
		}
	}
}
 
class ChicagoPizzaStore extends PizzaStore {
	public function createPizza($item) {
		if ($item == 'cheese') {
			return new ChicagoStyleCheesePizza();
		} elseif ($item == 'pepperoni') {
			return new ChicagoStylePepperoniPizza();
		} elseif ($item == 'clam') {
			return new ChicagoStyleClamPizza();
		} elseif ($item == 'veggie') {
			return new ChicagoStyleVeggiePizza();
		} else {
			return null;
		}
	}
}
 
abstract class Pizza {
	public $name;
	public $dough;
	public $sauce;
	public $toppings = array();
 
	public function prepare() {
		echo '<p>Preparing ' . $this->name . '<br />';
		echo '</p><p>Tossing dough ...</p>';
		echo '<p>Adding sauce ...</p>';
		echo '<p>Adding toppings: ';
		for ($i = 0; $i < count ($this->toppings); $i++) {
			echo ' ' . $this->toppings[$i];
		}
		echo '</ count></p>';
	}
 
	public function bake() {
		echo '<p>Bake for 25 minutes at 350</p>';
	}
 
	public function cut() {
		echo '<p>Cutting the pizza into diagonal slices</p>';
	}
 
	public function box() {
		echo '<p>Place pizza in official PizzaStore box</p>';
	}
 
	public function getName() {
		return $this->name;
	}
}
 
class NYStyleCheesePizza extends Pizza {
	public function __construct() {
		$this->name = 'NY Style Sauce and Cheese Pizza';
		$this->dough = 'Thin Crust Dough';
		$this->sauce = 'Marinara Sauce';
		$this->toppings[] = 'Grated Reggiano Cheese';
	}
}
 
class ChicagoStyleCheesePizza extends Pizza {
	public function __construct() {
		$this->name = 'Chicago Style Deep Dish Cheese Pizza';
		$this->dough = 'Extra Thick Crust Dough';
		$this->sauce = 'Plum Tomato Sauce';
		$this->toppings[] = 'Shredded Mozarella Cheese';
	}
 
	public function cut() {
		echo '<p>Cutting the pizza into square slices</p>';
	}
}
 
class PizzaTestDrive {
	public $NYStore;
	Public $ChicagoStore;
 
	public function __construct() {
		$this->NYStore = new NYPizzaStore();
		$this->ChicagoStore = new ChicagoPizzaStore();
 
		$pizza = $this->NYStore->orderPizza('cheese');
		echo '<p>Ethan ordered a ' . $pizza->getName() . '</p>';
 
		$pizza = $this->ChicagoStore->orderPizza('cheese');
		echo '<p>Joel ordered a ' . $pizza->getName() . '</p>';
	}
}
 
$ptd = new PizzaTestDrive();
?>

Output:
Preparing NY Style Sauce and Cheese Pizza
Tossing dough ...
Adding sauce ...
Adding toppings: Grated Reggiano Cheese
Bake for 25 minutes at 350
Cutting the pizza into diagonal slices
Place pizza in official PizzaStore box
Ethan ordered a NY Style Sauce and Cheese Pizza

Preparing Chicago Style Deep Dish Cheese Pizza
Tossing dough ...
Adding sauce ...
Adding toppings: Shredded Mozarella Cheese
Bake for 25 minutes at 350
Cutting the pizza into square slices
Place pizza in official PizzaStore box
Joel ordered a Chicago Style Deep Dish Cheese Pizza

Chapter 3: The decorator pattern

I have been meaning to put this out for a bit now. Without further ado, I welcome to you… the decorator pattern

<?php
abstract class Beverage {
	public $description = 'Unknown Beverage';
 
	public function getDescription() {
		return $this->description;
	}
 
	abstract public function cost();
}
 
// Note: This abstract class is totally unnecessary, but 
// I am leaving it in with the abstraction of the 
// getDescription method commented out as in PHP it is not 
// possible to abstract a method that is already defined
abstract class CondimentDecorator extends Beverage {
	//abstract public function getDescription();
}
 
class Espresso extends Beverage {
	public function __construct() {
		$this->description = 'Espresso';
	}
 
	public function cost() {
		return 1.99;
	}
}
 
class HouseBlend extends Beverage {
	public function __construct() {
		$this->description = 'House Blend Coffee';
	}
 
	public function cost() {
		return 0.89;
	}
}
 
class DarkRoast extends Beverage {
	public function __construct() {
		$this->description = 'Dark Roast';
	}
 
	public function cost() {
		return 0.99;
	}
}
 
class Decaf extends Beverage {
	public function __construct() {
		$this->description = 'Decaf Coffee';
	}
 
	public function cost() {
		return 1.05;
	}
}
 
class Mocha extends CondimentDecorator {
	public $beverage;
 
	public function __construct(Beverage $beverage) {
		$this->beverage = $beverage;
	}
 
	public function getDescription() {
		return $this->beverage->getDescription() . ', Mocha';
	}
 
	public function cost() {
		return $this->beverage->cost() + 0.20;
	}
}
 
class SteamedMilk extends CondimentDecorator {
	public $beverage;
 
	public function __construct(Beverage $beverage) {
		$this->beverage = $beverage;
	}
 
	public function getDescription() {
		return $this->beverage->getDescription() . ', Steamed Milk';
	}
 
	public function cost() {
		return $this->beverage->cost() + 0.10;
	}
}
 
class Soy extends CondimentDecorator {
	public $beverage;
 
	public function __construct(Beverage $beverage) {
		$this->beverage = $beverage;
	}
 
	public function getDescription() {
		return $this->beverage->getDescription() . ', Soy';
	}
 
	public function cost() {
		return $this->beverage->cost() + 0.15;
	}
}
 
class Whip extends CondimentDecorator {
	public $beverage;
 
	public function __construct(Beverage $beverage) {
		$this->beverage = $beverage;
	}
 
	public function getDescription() {
		return $this->beverage->getDescription() . ', Whip';
	}
 
	public function cost() {
		return $this->beverage->cost() + 0.10;
	}
}
 
class StarBuzzCoffee {
	public function __construct() {
		$beverage = new Espresso();
		echo $beverage->getDescription() . ' $' . $beverage->cost() . '<br />';
 
		$beverage2 = new DarkRoast();
		$beverage2 = new Mocha($beverage2);
		$beverage2 = new Mocha($beverage2);
		$beverage2 = new Whip($beverage2);
		echo $beverage2->getDescription() . ' $' . $beverage2->cost() . '<br />';
 
		$beverage3 = new HouseBlend();
		$beverage3 = new Soy($beverage3);
		$beverage3 = new Mocha($beverage3);
		$beverage3 = new Whip($beverage3);
		echo $beverage3->getDescription() . ' $' . $beverage3->cost() . '<br />';
	}
}
 
$coffee = new StarBuzzCoffee();

Output:
Espresso $1.99
Dark Roast, Mocha, Mocha, Whip $1.49
House Blend Coffee, Soy, Mocha, Whip $1.34

Chris Corbyn’s Controller and View tutorial

Ok, so every now and again a tutorial comes along that inspires learning on the first run of reading it. For example, Pádraic Brady has an excellent tutorial on challenge and response in the PHPDN forums. Last night I ran into one such tutorial by Chris Corbyn, author of the freaking outright awesome Swiftmailer library.

Chris has put together a very easy to learn instruction on using controllers and views, and it is worth a read. I followed the tutorial this morning and put together all the code he used in his examples. The code works right out of the box. And it helps you learn as well.

If you have a few minutes and an inclination to learn how controllers operate in conjunction with views, read the tutorial, implement the code and watch the magic happen. You will be pleasantly amazed.

Thanks Chris for a great tutorial.

Chapter 2: The Observer Pattern

Here is the first stage of the Observer pattern, chapter 2 of Head First Design Patterns:
(On a side note, I hate the way WordPress makes your post into what it thinks it should be. Wouldn’t you think that if I out text inside of a code that I wouldn’t want it auto-p’ed?)

< ?php
// All class names are preprended with Sample_ to prevent namespace clashing
// Need subject, concrete subject, observer and concrete observer
interface Sample_Subject
{
    public function registerObserver(Sample_Observer $o);
    public function removeObserver(Sample_Observer $o);
    public function notifyObservers();
}
 
interface Sample_Observer
{
    public function update($temp, $humidity, $pressure);
}
 
interface Sample_Display_Element
{
    public function display();
}
 
class Sample_Weather_Data implements Sample_Subject
{
    private $observers; // In the example, this is object-cast, then built in the constructor
    private $temperature = 0.00; // This casts as float
    private $humidity = 0.00;
    private $pressure = 0.00;
 
    public function __construct() {
        $this->observers = array();
    }
 
    public function registerObserver(Sample_Observer $o) {
        $this->observers[] = $o;
    }
 
    public function removeObserver(Sample_Observer $o) {
        // This has to be done totally different
        // Eventhough there is an ArrayObject in the SPL
        // It doesn't work like Java's
        foreach ($this->observers as $k => $v) {
            if ($v === $o) {
                unset($this->observers[$k]);
            }
        }
    }
 
    public function notifyObservers() {
        for ($i = 0; $i < count($this->observers); $i++) {
            $this->observers[$i]->update($this->temperature, $this->humidity, $this->pressure);
        }
    }
 
    public function measurementsChanged() {
        $this->notifyObservers();
    }
 
    // The book uses temperature, but it needs to be temp for PHP
    public function setMeasurements($temp, $humidity, $pressure) {
        $this->temperature = (float) $temp;
        $this->humidity = (float) $humidity;
        $this->pressure = (float) $pressure;
        $this->measurementsChanged();
    }
}
 
class Sample_Current_Conditions_Display implements Sample_Observer, Sample_Display_Element
{
    private $temperature;
    private $humidity;
    private $weatherData;
 
    public function __construct(Sample_Weather_Data $weatherData) {
        $this->weatherData = $weatherData;
        $this->weatherData->registerObserver($this);
    }
 
    public function update($temperature, $humidity, $pressure) {
        $this->temperature = (float) $temperature;
        $this->humidity = (float) $humidity;
        $this->display();
    }
 
    public function display() {
        echo "<p>Current conditions: " . $this->temperature . "F degrees and " . $this->humidity . "% humidity</p>";
    }
}
 
class Sample_Statistics_Display implements Sample_Observer, Sample_Display_Element
{
    private $maxTemp = 0.0; // Can't float and add F or it types to string
    private $minTemp = 200;
    private $tempSum = 0.0; // Again, no F
    private $numReadings = 0;
    private $weatherData;
 
    public function __construct(Sample_Weather_Data $weatherData) {
        $this->weatherData = $weatherData;
        $this->weatherData->registerObserver($this);
    }
 
    public function update($temp, $humidity, $pressure) {
        $this->tempSum += $temp;
        $this->numReadings++;
 
        if ($temp > $this->maxTemp) {
            $this->maxTemp = $temp;
        }
 
        if ($temp < $this->minTemp) {
            $this->minTemp = $temp;
        }
 
        $this->display();
    }
 
    public function display() {
        echo "<p>Avg/Max/Min temperature = " . $this->tempSum / $this->numReadings . "/" . $this->maxTemp . "/" . $this->minTemp . "</p>";
    }
}
 
class Sample_Forecast_Display implements Sample_Observer, Sample_Display_Element
{
    private $currentPressure = 29.92;
    private $lastPressure; // Float
    private $weatherData;
 
    public function __construct(Sample_Weather_Data $weatherData) {
        $this->weatherData = $weatherData;
        $this->weatherData->registerObserver($this);
    }
 
    public function update($temp, $humidity, $pressure) {
        $this->lastPressure = $this->currentPressure;
        $this->currentPressure = $pressure;
 
        $this->display();
    }
 
    public function display() {
        echo "<p>Forecast: ";
        if ($this->currentPressure > $this->lastPressure) {
            echo "Improving weather on the way!";
        } elseif ($this->currentPressure == $this->lastPressure) {
            echo "More of the same.";
        } elseif ($this->currentPressure < $this->lastPressure) {
            // This could have been an else
            echo "Watch out for cooler, rainy weather.";
        }
        echo "</p>";
    }
}
 
class Sample_Weather_Station 
{
    public $weatherData;
    public $currentConditionsDisplay;
    public $statisticsDisplay;
    public $forecastDisplay;
 
    public function __construct() {
        $this->weatherData = new Sample_Weather_Data();
        $this->currentConditionsDisplay = new Sample_Current_Conditions_Display($this->weatherData);
        $this->statisticsDisplay = new Sample_Statistics_Display($this->weatherData);
        $this->forecastDisplay = new Sample_Forecast_Display($this->weatherData);
 
        $this->weatherData->setMeasurements(80.0, 65.0, 30.4);
        $this->weatherData->setMeasurements(82.0, 70.0, 29.2);
        $this->weatherData->setMeasurements(78.0, 90.0, 29.2);
    }
}
 
$ws = new Sample_Weather_Station();
?>

Output:
Current conditions: 80F degrees and 65% humidity
Avg/Max/Min temperature = 80/80/80
Forecast: Improving weather on the way!
Current conditions: 82F degrees and 70% humidity
Avg/Max/Min temperature = 81/82/80
Forecast: Watch out for cooler, rainy weather.
Current conditions: 78F degrees and 90% humidity
Avg/Max/Min temperature = 80/82/78
Forecast: More of the same.

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