added FluentInterface

This commit is contained in:
Dominik Liebler
2011-08-28 13:19:23 +02:00
7 changed files with 510 additions and 4 deletions

View File

@ -0,0 +1,90 @@
<?php
namespace DesignPatterns;
/**
* chain of responsibilities pattern
*
* Purpose:
* to build a chain of objects to handle a call. if one object cannot handle a call, it delegates the call to the next
* in the chain and so forth
*
* Examples:
* - Caching: first object is an instance of e.g. a Memcached Interface, if that "misses" it delegates the call to the
* Database Interface
*
*/
interface KeyValueStorage
{
public function get($key);
}
class SlowStorage implements KeyValueStorage
{
protected $_data = array();
public function __construct($data = array())
{
$this->_data = $data;
}
/**
* this class has no next handler, so it MUST be able to handle all requests
*
* @param $key
* @return mixed
*/
public function get($key)
{
return $this->_data[$key];
}
}
class FastStorage implements KeyValueStorage
{
/**
* @var array
*/
protected $_data;
/**
* the next handler in the chain
*
* @var \DesignPatterns\KeyValueStorage
*/
protected $_nextHandler;
public function __construct(array $data, KeyValueStorage $nextHandler)
{
$this->_data = $data;
$this->_nextHandler = $nextHandler;
}
/**
* when this storage gets a "miss", search in next handler
*
* @param $key
* @return mixed
*/
public function get($key)
{
if (true /* miss */) {
// delegate the call to the next handler in the chain
return $this->_nextHandler->get($key);
}
}
}
// BUILD THE STORAGES AND CHAIN
$slowStorage = new SlowStorage(array('foo' => 'bar'));
$fastStorage = new FastStorage(array('bar' => 'baz'), $slowStorage);
$fastStorage->get('foo'); // will be handled by SlowStorage
$fastStorage->get('bar'); // will be handled by FastStorage
/**
* In this example we could also add a abstract class and extend it to build Fast- and SlowStorage. That class would
* then manage the chain and when the cache hits a "miss", it would check if there is a next handler
*/

View File

@ -6,13 +6,12 @@ namespace DesignPatterns;
* composite pattern
*
* Purpose:
* to treat a group of objects the same way as a single instance of the object, it establishes 1-to-n relationships
* between objects
* to treat a group of objects the same way as a single instance of the object
*
* Example:
* - a form class instance handles all its form elements like a single instance of the form, when render() is called, it
* subsequently runs trough all its child elements and calls render() on them
* - a class to process files would process it by running process on all row objects in the file
* subsequently runs trough all its child elements and calls render() on them
* - Zend_Config: a tree of configuration options, each one is a Zend_Config object
*
*/
class Form

84
Decorator/Decorator.php Normal file
View File

@ -0,0 +1,84 @@
<?php
namespace DesignPatterns;
/**
* Decorator pattern
*
* Purpose:
* to dynamically add new functionality to class instances
*
* Examples:
* - Zend Framework: decorators for Zend_Form_Element instances
* - Web Service Layer: Decorators JSON and XML for a REST service (in this case, only one of these should be allowed of
* course)
*
*/
class Webservice
{
protected $_data;
/**
* an array to holds all added decorators, often there would be defaults set in this
* array, e.g. the service could be defaulted to use JSON and only for special APIs
* use XML
*
* @var array
*/
protected $_decorators = array();
public function __construct($data)
{
$this->_data = $data;
}
/**
*
*
* @param WebserviceDecorator $decorator
* @return void
*/
public function addDecorator(WebserviceDecorator $decorator)
{
$this->_decorators[] = $decorator;
}
/**
* @return string
*/
public function renderData()
{
$response = '';
foreach ($this->_decorators as $decorator) {
$response = $decorator->renderData($this->_data);
}
return $response;
}
}
interface WebserviceDecorator
{
public function renderData($data);
}
class JsonDecorator implements WebserviceDecorator
{
public function renderData($data)
{
return json_encode($data);
}
}
class XmlDecorator implements WebserviceDecorator
{
public function renderData($data)
{
// do some fany conversion to xml from array ...
return simplexml_load_string($data);
}
}
$service = new Webservice(array('foo' => 'bar'));
$service->addDecorator(new JsonDecorator());
echo $service->renderData();

View File

@ -0,0 +1,59 @@
<?php
namespace DesignPatterns;
/**
* fluent interface pattern
*
* Purpose:
* to write code that is easy readable just like "real" sentences
*
* Examples:
* - Doctrine2's QueryBuilder works something like that example class below
* - PHPUnit uses fluent interfaces to build mock objects
*
*/
class SQL
{
protected $_fields = array();
protected $_from = array();
protected $_where = array();
/**
*
* @param array $fields
* @return SQL
*/
public function select(array $fields = array())
{
$this->_fields = $fields;
return $this;
}
/**
*
* @param string $table
* @param string $alias
* @return SQL
*/
public function from($table, $alias)
{
$this->_from[] = $table . ' AS ' . $alias;
return $this;
}
/**
* @param string $condition
* @return SQL
*/
public function where($condition)
{
$this->_where[] = $condition;
return $this;
}
}
$instance = new SQL();
$instance->select(array('foo', 'bar'))
->from('foobar', 'f')
->where('f.bar = ?');

109
Iterator/Iterator.php Normal file
View File

@ -0,0 +1,109 @@
<?php
namespace DesignPatterns;
/**
* iterator pattern
*
* Purpose:
* to make an object iterable
*
* Examples:
* - to process a file line by line by just running over all lines (which have an object representation) for a file
* (which of course is an object, too)
*
* Note:
* Standard PHP Library (SPL) defines an interface Iterator which is best suited for this!
* Often you would want to implement the Countable interface too, to allow count($object) on your iterable object
*
* THIS EXAMPLE ALSO APPLIES THE COMPOSITE PATTERN
*
*/
class File
{
protected $_rowset;
protected $_pathName;
public function __construct($pathName)
{
$this->_rowset = new Rowset($this);
}
public function process()
{
$this->_rowset->process();
}
}
class Rowset implements Iterator
{
protected $_currentRow;
protected $_file;
public function __construct($file)
{
$this->_file = $file;
}
/**
* composite pattern: run through all rows and process them
*
* @return void
*/
public function process()
{
// this actually calls rewind(), { next(), valid(), key() and current() :}
foreach ($this as $line => $row) {
$row->process();
}
}
public function rewind()
{
// seek to first line from $this->_file
}
public function next()
{
// read the next line from $this->_file
if (!$eof) {
$data = ''; // get the line
$this->_currentRow = new Row($data);
} else {
$this->_currentRow = null;
}
}
public function current()
{
return $this->_currentRow;
}
public function valid()
{
return null !== $this->_currentRow;
}
public function key()
{
// you would want to increment this in next() or whatsoever
return $this->_lineNumber;
}
}
class Row
{
protected $_data;
public function __construct($data)
{
$this->_data = $data;
}
public function process()
{
// do some fancy things here ...
}
}

71
Observer/Observer.php Normal file
View File

@ -0,0 +1,71 @@
<?php
namespace DesignPatterns;
/**
* Observer pattern
*
* Purpose:
* to implement a publish/subscribe behaviour to an object, whenever a "Subject" object changes it's state, the attached
* "Observers" will be notified. It is used to shorten the amount of coupled objects and uses loose coupling instead
*
* Examples:
* - a message queue system is observed to show the progress of a job in a GUI
*
* PHP already defines two interfaces that can help to implement this pattern: SplObserver and SplSubject
*
*/
class UserObserver implements \SplObserver
{
public function update(\SplSubject $subject)
{
echo get_class($subject) . ' has been updated';
}
}
class User implements \SplSubject
{
/**
* @var array
*/
protected $_observers = array();
/**
* attach a new observer
*
* @param \SplObserver $observer
* @return void
*/
public function attach(\SplObserver $observer)
{
$this->_observers[] = $observer;
}
/**
* detach an observer
*
* @param \SplObserver $observer
* @return void
*/
public function detach(\SplObserver $observer)
{
$index = array_search($observer, $this->_observers);
if (false !== $index) {
unset($this->_observers[$index]);
}
}
/**
*
*
* @return void
*/
public function notify()
{
/** @var SplObserver $observer */
foreach ($this->_observers as $observer) {
$observer->update($this);
}
}
}

94
Proxy/Proxy.php Normal file
View File

@ -0,0 +1,94 @@
<?php
namespace DesignPatterns;
/**
* Proxy pattern
*
* Purpose:
* to interface to anything that is expensive or impossible to duplicate
*
* Examples:
* - Doctrine2 uses proxies to implement framework magic (e.g. Lazy initialization) in them, while the user still works
* with his own entity classes and will never use nor touch the proxies
*
*/
class Record
{
protected $_data;
public function __construct($data = null)
{
$this->_data = $data;
}
/**
* magic setter
*
* @param string $name
* @param mixed $value
* @return void
*/
public function __set($name, $value)
{
$this->_data[(string) $name] = $value;
}
/**
* magic getter
*
* @param string $name
* @return mixed|null
*/
public function __get($name)
{
if (array_key_exists($name, $this->_data)) {
return $this->_data[(string) $name];
} else {
return null;
}
}
}
class RecordProxy extends Record
{
/**
* @var bool
*/
protected $_isDirrty = false;
/**
* @var bool
*/
protected $_isInitialized = false;
/**
* @param array
*/
public function __construct($data)
{
parent::__construct($data);
// when the record has data, mark it as initialized
// since Record will hold our business logic, we don't want to
// implement this behaviour there, but instead in a new proxy class
// that extends the Record class
if (null !== $data) {
$this->_isInitialized = true;
$this->_isDirrty = true;
}
}
/**
* magic setter
*
* @param string $name
* @param mixed $value
* @return void
*/
public function __set($name, $value)
{
$this->_isDirrty = true;
parent::__set($name, $value);
}
}