Recent Posts
Design Patterns: Observer
One of my favorite design patterns is the observer. Like most patterns, this serves to encourage developers to create loosely coupled code. Properly implemented, the observer allows you work with a single object and inform related processes of changes that may require action to be taken. One of the places where this can be incredibly useful is in committing models to storage. Think about it – there’s a good chance you aren’t just saving and retrieving information to a database. In many of the applications that I’ve dealt with, saving information in the modeling layer means I’ll need to deal with caching, search, email notifications, statistics, and more. Trying to cram all of this into the data access layer might look something like this:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
class DataAccess {
/**
* Save some data
* @param Model $model
*/
public function save(Model $model) {
echo "\nI just saved $model";
// Now I'll need to commit this information to the cache layer
// Blah blah blah code
// Next I need to inform the search service that some data has changed
// Blah blah blah code
// Now I need to send an email to inform a subscriber that something has changed
// Blah blah blah code
// Now I need to calculate some stats that reflect this change
// Blah blah blah code
}
/**
* Delete some data
* @param Model $model
*/
public function delete(Model $model) {
echo "\nI just deleted $model";
// Now I'll need to commit this information to the cache layer
// Blah blah blah code
// Next I need to inform the search service that some data has changed
// Blah blah blah code
// Now I need to send an email to inform a subscriber that something has changed
// Blah blah blah code
// Now I need to calculate some stats that reflect this change
// Blah blah blah code
}
} |
As you can imagine, once you replace all of the comments above with actual code, these methods can get pretty heavy. This is far from the OO ideal of a single purpose for a single class. Now what happens when you have to save a large series of records? How could you take a part of this process and make it asynchronous? The observer pattern comes to the rescue. For this example, I’ve chosen to implement the Standard PHP Library’s built in observer interfaces.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 |
class DataAccess implements SplObserver {
/**
* Save some data
* @param Model $model
*/
public function save(Model $model) {
echo "\nI just saved $model";
}
/**
* Delete some data
* @param Model $model
*/
public function delete(Model $model) {
echo "\nI just deleted $model";
}
/*
* (non-PHPdoc) @see SplObserver::update()
*/
public function update(SplSubject $model) {
$this->save ( $model );
}
}
// Just a generic model for the example
class Model implements SplSubject {
private $_properties = array ();
private $_observers = array ();
/*
* generically handle properties: you wouldn't want to do it quite like this
* for a real world scenario
*/
public function __get($name) {
return $this->_properties [$name];
}
public function __set($name, $value) {
$this->_properties [$name] = $value;
}
public function __call($method, $args) {
if (strpos ( $method, 'get' ) === 0) {
$name = lcfirst ( str_replace ( 'get', '', $method ) );
return $this->_properties [$name];
}
if (strpos ( $method, 'set' ) === 0) {
$name = lcfirst ( str_replace ( 'set', '', $method ) );
$this->_properties [$name] = $args [0];
return $this;
}
}
public function __toString() {
return __CLASS__ . ' Object: ' . print_r ( $this->_properties, true );
}
/*
* (non-PHPdoc) @see SplSubject::attach()
*/
public function attach(SplObserver $observer) {
$this->_observers [] = $observer;
return $this;
}
/*
* (non-PHPdoc) @see SplSubject::detach()
*/
public function detach(SplObserver $observer) {
foreach ( $this->_observers as $k => $v ) {
if ($v === $observer) {
unset ( $this->_observers [$k] );
}
}
return $this;
}
/*
* (non-PHPdoc) @see SplSubject::notify()
*/
public function notify() {
foreach ( $this->_observers as $observer ) {
$observer->update ( $this );
}
}
public function commit() {
$this->notify ();
}
} |
Now adding additional observers to handle data changes is easy. We can add a few more classes and do something like this:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 |
class Cache implements SplObserver {
/**
* Save some data
* @param Model $model
*/
public function save(Model $model) {
echo "\nI just cached $model";
}
/**
* Delete some data
* @param Model $model
*/
public function delete(Model $model) {
echo "\nI just cached $model";
}
/*
* (non-PHPdoc) @see SplObserver::update()
*/
public function update(SplSubject $model) {
$this->save ( $model );
}
}
class SearchIndex implements SplObserver {
/**
* Save some data
* @param Model $model
*/
public function save(Model $model) {
echo "\nI just indexed $model";
}
/**
* Delete some data
* @param Model $model
*/
public function delete(Model $model) {
echo "\nI just removed $model from the index";
}
/*
* (non-PHPdoc) @see SplObserver::update()
*/
public function update(SplSubject $model) {
$this->save ( $model );
}
}
$da = new DataAccess ();
$cache = new Cache ();
$searchIndex = new SearchIndex ();
$model = new Model ();
$model->setName ( 'Joshua Kaiser' )
->setAge ( 32 )
->setOccupation ( 'Software Engineer' )
->attach ( $da )
->attach ( $searchIndex )
->attach ( $cache )
->commit (); |
Output:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
php Observer.php
I just saved Model Object: Array
(
[name] => Joshua Kaiser
[age] => 32
[occupation] => Software Engineer
)
I just indexed Model Object: Array
(
[name] => Joshua Kaiser
[age] => 32
[occupation] => Software Engineer
)
I just cached Model Object: Array
(
[name] => Joshua Kaiser
[age] => 32
[occupation] => Software Engineer
) |
So now with observers, you can create a workflow for your data specific to the situation. Add as much or as little as you need to. Each of the observers can go off and do what they need, such as engaging third party services like Gearman to create asynchronous offline tasks. What’s more, all of these are much smaller, simpler classes that don’t need to know anything about what is going on elsewhere in the application to perform their given tasks.
read moreDesign Patterns: Singleton
The singleton design pattern is probably one of the most basic patterns in use. Many consider this an anti-pattern because developers tend to over-use it and create code that is really just procedural code in disguise of OO. Still, this pattern can be useful in cases where the developer would like to reuse a single instance of an object with expensive resources, such as a database connection.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
<?php
class MySingleton {
// Singletons feature a static property that
// stores an instance of the class object
private static $_instance = null;
// Singletons also have a private constructor.
// This is to prevent new instances of
// the object from being created when they aren't wanted.
private function __construct () {}
// Now we need a static method to construct the object if
// needed and return a reference to it
public static function getInstance() {
if (is_null($this->_instance)) {
$this->_instance = new MySingleton();
}
return $this->_instance;
}
} |
Now in order to use the object, you use the static instance method to retrieve it:
|
1 2 |
$mySingletonObject = MySingleton::getInstance();
$mySingletonObject->doSomeStuff(); |
An interesting alternative to the singleton pattern is to use a dependency injection container (DIC). The DIC works by keeping a registry of instantiated objects and providing them to various parts of the application upon request. See http://framework.zend.com/manual/2.0/en/modules/zend.di.introduction.html
read moreMethod Chaining in PHP
I saw this come across Stack Overflow today, and wanted to elaborate. Method chaining is a particularly useful technique in modeling objects where the developer may call a series of methods that typically have no return value. Let’s use a model Person object as an example:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
class Person {
private $_firstName;
private $_lastName;
private $_gender;
private $_weight;
public function setFirstName($value) {
$this->_firstName = $value;
}
public function setLastName($value) {
$this->_lastName = $value;
}
public function setGender($value) {
$this->_gender = $value;
}
public function setWeight($value) {
$this->_weight = $value;
}
} |
Now if we wanted to set all of the properties of a newly instantiated Person object, we need to split the calls up like so:
|
1 2 3 4 5 |
$person = new Person();
$person->setFirstName('Joshua');
$person->setLastName('Kaiser');
$person->setGender('Male');
$person->setWeight('170'); |
But I like to type less. That $person in there happens over and over again. Let’s make it go away. Revise the Person class, adding “return $this;” to the setter methods:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
class Person {
private $_firstName;
private $_lastName;
private $_gender;
private $_weight;
public function setFirstName($value) {
$this->_firstName = $value;
return $this;
}
public function setLastName($value) {
$this->_lastName = $value;
return $this;
}
public function setGender($value) {
$this->_gender = $value;
return $this;
}
public function setWeight($value) {
$this->_weight = $value;
return $this;
}
} |
Now when we set the same properties as above, we can skip the reference to the object over and over:
|
1 2 3 4 5 |
$person = new Person();
$person->setFirstName('Joshua');
->setLastName('Kaiser');
->setGender('Male');
->setWeight('170'); |
read more
Use Social Instead of Email to Boost Productivity
I walked into work this morning to find more than 160 new messages waiting for me from just the past 3 days (that includes the weekend). Add to that, the 300 or so (internally generated) emails sitting in my junk mail folder. The majority of these messages have little or nothing of interest to me. They don’t effect my responsibilities today in any meaningful way or give me any information that helps me to be better at what I do. However, I have to spend time sifting through each of them in order to find the small handful that actually matter. The time wasted is significant. The potential for lost information is even higher.
Josh is a software engineer, dad, musician, and rock climber. He loves all things tech and all things outdoors. You can generally find him climbing some rocks, doing crazy classes at the rock gym, coding, or doing something geeky.