a REAL abstract factory not a helper-like full of static

This commit is contained in:
Trismegiste
2013-05-10 23:54:19 +02:00
parent 3a3937f339
commit eebdbc1dc5
11 changed files with 216 additions and 24 deletions

View File

@@ -1,42 +1,43 @@
<?php <?php
namespace DesignPatterns; namespace DesignPatterns\AbstractFactory;
/** /**
* Abstract Factory pattern * Abstract Factory pattern
* *
* Purpose: * Purpose:
* to create series of related or dependant objects without specifying their concrete classes, * to create series of related or dependant objects without specifying their concrete classes,
* usually the created classes all implement the same interface * usually the created classes all implement the same interface. The client of the abstract
* * factory does not care about how these objects are created, he just knows they goes together.
* Examples: *
* - A Factory to create media in a CMS: classes would be text, audio, video, picture * Sometimes also known as "Kit" in a GUI libraries.
* - SQL Factory (types are all strings with SQL, but they vary in detail (tables, fields, etc.)) *
* - Zend Framework: Zend_Form::createElement() creates form elements, but you could also call new T * This design pattern implements the Dependency Inversion Principle since
* TextElement() instead * it is the concrete subclass which creates concrete components.
* - an abstract factory to create various exceptions (e.g. Doctrine2 uses this method) *
* * In this case, the abstract factory is a contract for creating some components
* for the web. There are two components : Text and Picture. There is two ways
* of rendering : HTML or JSON.
*
* Therefore 4 concretes classes, but the client just need to know this contract
* to build a correct http response (for a html page or for an ajax request)
*/ */
abstract class AbstractFactory abstract class AbstractFactory
{ {
/** /**
* @static * Creates a text component
*
* @param string $content * @param string $content
* @return AbstractFactory\Text * @return Text
*/ */
public static function createText($content) abstract public function createText($content);
{
return new AbstractFactory\Text($content);
}
/** /**
* @static * Createss a picture component
*
* @param string $path * @param string $path
* @param string $name * @param string $name
* @return AbstractFactory\Picture * @return Picture
*/ */
public static function createPicture($path, $name = '') abstract public function createPicture($path, $name = '');
{
return new AbstractFactory\Picture($path, $name);
}
} }

View File

@@ -0,0 +1,23 @@
<?php
/*
* DesignPatternPHP
*/
namespace DesignPatterns\AbstractFactory\Html;
use DesignPatterns\AbstractFactory\Picture as BasePicture;
/**
* Picture is a concrete image for HTML rendering
*/
class Picture extends BasePicture
{
// some crude rendering from HTML output
public function render()
{
return sprintf('<img src="%s" title="$s"/>', $this->_path, $this->_name);
}
}

View File

@@ -0,0 +1,22 @@
<?php
/*
* DesignPatternPHP
*/
namespace DesignPatterns\AbstractFactory\Html;
use DesignPatterns\AbstractFactory\Text as BaseText;
/**
* Text is a concrete text for HTML rendering
*/
class Text extends BaseText
{
public function render()
{
return "<div>" . htmlspecialchars($this->_text) . '</div>';
}
}

View File

@@ -0,0 +1,25 @@
<?php
/*
* DesignPatternPHP
*/
namespace DesignPatterns\AbstractFactory;
/**
* HtmlFactory is a concrete factory for HTML component
*/
class HtmlFactory extends AbstractFactory
{
public function createPicture($path, $name = '')
{
return new Html\Picture($path, $name);
}
public function createText($content)
{
return new Html\Text($content);
}
}

View File

@@ -0,0 +1,23 @@
<?php
/*
* DesignPatternPHP
*/
namespace DesignPatterns\AbstractFactory\Json;
use DesignPatterns\AbstractFactory\Picture as BasePicture;
/**
* Picture is a concrete image for JSON rendering
*/
class Picture extends BasePicture
{
// some crude rendering from JSON output
public function render()
{
return json_encode(array('title' => $this->_name, 'path' => $this->_path));
}
}

View File

@@ -0,0 +1,22 @@
<?php
/*
* DesignPatternPHP
*/
namespace DesignPatterns\AbstractFactory\Json;
use DesignPatterns\AbstractFactory\Text as BaseText;
/**
* Text is a text component with a JSON rendering
*/
class Text extends BaseText
{
public function render()
{
return json_encode(array('content' => $this->_text));
}
}

View File

@@ -0,0 +1,26 @@
<?php
/*
* DesignPatternPHP
*/
namespace DesignPatterns\AbstractFactory;
/**
* JsonFactory is a factory for creating a family of JSON component
* (example for ajax)
*/
class JsonFactory extends AbstractFactory
{
public function createPicture($path, $name = '')
{
return new Json\Picture($path, $name);
}
public function createText($content)
{
return new Json\Text($content);
}
}

View File

@@ -2,7 +2,12 @@
namespace DesignPatterns\AbstractFactory; namespace DesignPatterns\AbstractFactory;
/**
* This contract is not part of the pattern, in general case, each component
* are not related
*/
interface Media interface Media
{ {
function render();
} }

View File

@@ -2,7 +2,7 @@
namespace DesignPatterns\AbstractFactory; namespace DesignPatterns\AbstractFactory;
class Picture implements Media abstract class Picture implements Media
{ {
protected $_path; protected $_path;
protected $_name; protected $_name;

View File

@@ -2,7 +2,7 @@
namespace DesignPatterns\AbstractFactory; namespace DesignPatterns\AbstractFactory;
class Text implements Media abstract class Text implements Media
{ {
/** /**
* *

View File

@@ -0,0 +1,45 @@
<?php
/*
* DesignPatternPHP
*/
namespace DesignPatterns\Tests\AbstractFactory;
use DesignPatterns\AbstractFactory\AbstractFactory;
use DesignPatterns\AbstractFactory\HtmlFactory;
use DesignPatterns\AbstractFactory\JsonFactory;
/**
* AbstractFactoryTest tests concrete factories
*/
class AbstractFactoryTest extends \PHPUnit_Framework_TestCase
{
public function getFactories()
{
return array(
array(new JsonFactory()),
array(new HtmlFactory())
);
}
/**
* This is the client of factories. Note that the client does not
* care which fatory is given to him, it can create any component he
* want and render how he wants.
*
* @dataProvider getFactories
*/
public function testComponentCreation(AbstractFactory $factory)
{
$article = array(
$factory->createText('Lorem Ipsum'),
$factory->createPicture('/image.jpg', 'caption'),
$factory->createText('footnotes')
);
$this->assertContainsOnly('DesignPatterns\AbstractFactory\Media', $article);
}
}