mirror of
https://github.com/DesignPatternsPHP/DesignPatternsPHP.git
synced 2025-07-10 01:46:22 +02:00
added FluentInterface
This commit is contained in:
90
ChainOfResponsibilities/ChainOfResponsibilities.php
Normal file
90
ChainOfResponsibilities/ChainOfResponsibilities.php
Normal 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
|
||||
*/
|
@ -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
84
Decorator/Decorator.php
Normal 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();
|
59
FluentInterface/FluentInterface.php
Normal file
59
FluentInterface/FluentInterface.php
Normal 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
109
Iterator/Iterator.php
Normal 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
71
Observer/Observer.php
Normal 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
94
Proxy/Proxy.php
Normal 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);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user