A library for the strategy pattern in PHP, if you use Symfony2 you could easily integrate Godfather with the bundle. |
---|
http://en.wikipedia.org/wiki/Strategy_pattern
Define a family of algorithms, encapsulate each one, and make them interchangeable. Strategy lets the algorithm vary independently from clients that use it.
Given an object, you want to know its service.
eg. Entity\Mug
has a MugService
and Entity\Tshirt
has a TshirtService
$product = random(0,1)? new Entity\Mug: new Entity\Product
$productService = $godfather->getStrategy('service', $product);
// also works with
$productService = $godfather->getService($product);
echo get_class($productService);
// will be randomly TshirtService or MugService
A working example is at example/godfather.php
cd example
php godfather.php
- If you have a lot of classes that differs by their behaviour...
- If you have multiple conditional statements in order to define different behaviours...
- Given an object you want to know its manager/service/handler/provider/repository/...
composer require pugx/godfather ~0.1
The problem is that you have an object and you want to handle it properly.
This library does not try to duplicate the services, or to create a new container, but uses aliases in order to have a mapping between services and names.
An object is converted by the Context::getStrategyName
more info at changing the Context::Converter.
If your code look like this you will need the godfather's protection :)
// Pseudo Code
class Cart
function add(ProductInterface $product, OptionsInterface $options)
{
if ($product instanceOf Mug) {
$item = $mugManager->add($options);
}
if ($product instanceOf Tshirt) {
$item = $tshirtManager->add($options);
}
// ...
}
// Pseudo Code
class Cart
function add(ProductInterface $product, OptionsInterface $options)
{
$item = $this->godfather->getManager($product)->add($options);
// ...
}
$container = new Container\ArrayContainerBuilder();
$container->set('mug_service', new Your\MugService);
$container->set('tshirt_service', new Your\TshirtService);
$godfather = new Godfather($container, 'godfather');
$godfather->addStrategy('service', 'Mug', 'mug_service');
$godfather->addStrategy('service', 'Tshirt', 'tshirt_service');
// Step2. usage
class Cart
public function __construct($godfather)
//...
public function add(ProductInterface $product, OptionsInterface $options)
{
// get the strategy for cart with the context $product
$service = $this->godfather->getStrategy('service', $product);
// or $strategy = $this->godfather->getCart($product);
return $strategy->addToCart($product, $options);
}
$container = new Container\ArrayContainerBuilder();
$loader = new Loader\XmlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config'));
$loader->load('services.xml');
$godfather = new Godfather($container, 'godfather');
// Step2. usage
class Cart
public function __construct($godfather)
//...
public function add(ProductInterface $product, OptionsInterface $options)
{
// get the strategy for cart with the context $product
$service = $this->godfather->getStrategy('service', $product);
// or $strategy = $this->godfather->getService($product);
Add the bundle in the app/AppKernel.php
class AppKernel extends Kernel
{
public function registerBundles()
{
$bundles = array(
...
new PUGX\GodfatherBundle\GodfatherBundle(),
# add the below configuration only if you need to specify the fallback or the interface.
godfather:
default:
contexts:
manager: ~ # your strategy name
# add the below configuration only if you need to specify the fallback or the interface.
godfather:
default:
contexts:
manager:
fallback: manager_standard # need a reference to a defined service
parameters:
mug.class: Mug
tshirt.class: Tshirt
services:
manager_standard:
class: StandardProductManager
manager_mug:
class: MugManager
tags:
- { name: godfather.strategy, context_name: 'manager', context_key: %mug.class% }
manager_tshirt:
class: TshirtManager
tags:
- { name: godfather.strategy, context_name: 'manager', context_key: %tshirt.class% }
$product = new \Product\ShoeProduct();
$manager = $container->get('godfather')->getManager($product);
// or $manager = $container->get('godfather')->getStrategy('manager', $product);
// then $manager->doSomethingGreat();
Instead of default you could configure your strategy in different godfather instances.
godfather:
death:
contexts:
manager: ~
life:
contexts:
manager: ~
the strategies:
services:
manager.entity_life:
class: EntityProductManager
arguments: ['life']
tags:
- { name: godfather.strategy, instance:'life', context_name: 'manager', context_key: %product.show.class% }
manager.entity_death:
class: EntityProductManager
arguments: ['death']
tags:
- { name: godfather.strategy, instance:'death', context_name: 'manager', context_key: %product.show.class% }
and then the code with multiple instances
$this->getContainer('godfather.life')->getManager($entity);
$this->getContainer('godfather.death')->getManager($entity);
The Godfather\Context\Context::getStrategyName
transforms an object into a strategy name,
the default one just extract from the $object the short class name.
If you want another converter create your class extends the ContextInterface and then:
godfather:
deafault:
contexts:
manager:
class: \My\Context
Active contribution and patches are very welcome. To keep things in shape we have quite a bunch of unit tests. If you're submitting pull requests please make sure that they are still passing and if you add functionality please take a look at the coverage as well it should be pretty high :)
composer create-project pugx/godfather --dev -s dev
cd godfather
bin/phpunit