AbstractFactoryStructural.php: Приклад структури патерну

<?php
namespace RefactoringGuru\AbstractFactory\Structural;
/**
* The Abstract Factory interface declares a set of methods that return
* different abstract products. These products are called a family and are
* related by a high-level theme or concept. Products of one family are usually
* able to collaborate among themselves. A family of products may have several
* variants, but the products of one variant are incompatible with products of
* another.
*/
interface AbstractFactory
{
public function createProductA(): AbstractProductA;
public function createProductB(): AbstractProductB;
}
/**
* Concrete Factories produce a family of products that belong to a single
* variant. The factory guarantees that resulting products are compatible. Note
* that signatures of the Concrete Factory's methods return an abstract product,
* while inside the method a concrete product is instantiated.
*/
class ConcreteFactory1 implements AbstractFactory
{
public function createProductA(): AbstractProductA
{
return new ConcreteProductA1();
}
public function createProductB(): AbstractProductB
{
return new ConcreteProductB1();
}
}
/**
* Each Concrete Factory has a corresponding product variant.
*/
class ConcreteFactory2 implements AbstractFactory
{
public function createProductA(): AbstractProductA
{
return new ConcreteProductA2();
}
public function createProductB(): AbstractProductB
{
return new ConcreteProductB2();
}
}
/**
* Each distinct product of a product family should have a base interface. All
* variants of the product must implement this interface.
*/
interface AbstractProductA
{
public function usefulFunctionA(): string;
}
/**
* Concrete Products are created by corresponding Concrete Factories.
*/
class ConcreteProductA1 implements AbstractProductA
{
public function usefulFunctionA(): string
{
return "The result of the product A1.";
}
}
class ConcreteProductA2 implements AbstractProductA
{
public function usefulFunctionA(): string
{
return "The result of the product A2.";
}
}
/**
* Here's the the base interface of another product. All products can interact
* with each other, but proper interaction is possible only between products of
* the same concrete variant.
*/
interface AbstractProductB
{
/**
* Product B is able to do its own thing...
*/
public function usefulFunctionB(): string;
/**
* ...but it also can collaborate with the ProductA.
*
* The Abstract Factory makes sure that all products it creates are of the
* same variant and thus, compatible.
*/
public function anotherUsefulFunctionB(AbstractProductA $collaborator): string;
}
/**
* Concrete Products are created by corresponding Concrete Factories.
*/
class ConcreteProductB1 implements AbstractProductB
{
public function usefulFunctionB(): string
{
return "The result of the product B1.";
}
/**
* The variant, Product B1, is only able to work correctly with the variant,
* Product A1. Nevertheless, it accepts any instance of AbstractProductA as
* an argument.
*/
public function anotherUsefulFunctionB(AbstractProductA $collaborator): string
{
$result = $collaborator->usefulFunctionA();
return "The result of the B1 collaborating with the ({$result})";
}
}
class ConcreteProductB2 implements AbstractProductB
{
public function usefulFunctionB(): string
{
return "The result of the product B2.";
}
/**
* The variant, Product B2, is only able to work correctly with the variant,
* Product A2. Nevertheless, it accepts any instance of AbstractProductA as
* an argument.
*/
public function anotherUsefulFunctionB(AbstractProductA $collaborator): string
{
$result = $collaborator->usefulFunctionA();
return "The result of the B2 collaborating with the ({$result})";
}
}
/**
* The client code works with factories and products only through abstract
* types: AbstractFactory and AbstractProduct. This lets you pass any factory or
* product subclass to the client code without breaking it.
*/
function clientCode(AbstractFactory $factory)
{
$product_a = $factory->createProductA();
$product_b = $factory->createProductB();
print($product_b->usefulFunctionB() . "\n");
print($product_b->anotherUsefulFunctionB($product_a) . "\n");
}
/**
* The client code can work with any concrete factory class.
*/
print("Client: Testing client code with the first factory type:\n");
clientCode(new ConcreteFactory1());
print("\n");
print("Client: Testing the same client code with the second factory type:\n");
clientCode(new ConcreteFactory2());

Output.txt: Результат виконання

Client: Testing client code with the first factory type:
The result of the product B1.
The result of the B1 collaborating with the (The result of the product A1.)
Client: Testing the same client code with the second factory type:
The result of the product B2.
The result of the B2 collaborating with the (The result of the product A2.)

Приклад: Приклад з життя

AbstractFactoryRealWorld.php: Приклад з життя

<?php
namespace RefactoringGuru\AbstractFactory\RealWorld;
/**
* Abstract Factory Design Pattern
*
* Intent: Provide an interface for creating families of related or dependent
* objects without specifying their concrete classes.
*
* Example: In this example, the Abstract Factory pattern provides an
* infrastructure for creating various types of templates for different elements
* of a web page.
*
* A web application can support different rendering engines at the same time,
* but only if its classes are independent of the concrete classes of rendering
* engines. Hence, the application's objects must communicate with template
* objects only via their abstract interfaces. Your code should not create the
* template objects directly, but delegate their creation to special factory
* objects. Finally, your code should not depend on the factory objects either
* but, instead, should work with them via the abstract factory interface.
*
* As a result, you will be able to provide the app with the factory object that
* corresponds to one of the rendering engines. All templates, created in the
* app, will be created by that factory and their type will match the type of
* the factory. If you decide to change the rendering engine, you'll be able to
* pass a new factory to the client code, without breaking any existing code.
*/
/**
* The Abstract Factory interface declares creation methods for each distinct
* product type.
*/
interface TemplateFactory
{
public function createTitleTemplate(): TitleTemplate;
public function createPageTemplate(): PageTemplate;
}
/**
* Each Concrete Factory corresponds to a specific variant (or family) of
* products.
*
* This Concrete Factory creates Twig templates.
*/
class TwigTemplateFactory implements TemplateFactory
{
public function createTitleTemplate(): TitleTemplate
{
return new TwigTitleTemplate();
}
public function createPageTemplate(): PageTemplate
{
return new TwigPageTemplate($this->createTitleTemplate());
}
}
/**
* And this Concrete Factory creates PHPTemplate templates.
*/
class PHPTemplateFactory implements TemplateFactory
{
public function createTitleTemplate(): TitleTemplate
{
return new PHPTemplateTitleTemplate();
}
public function createPageTemplate(): PageTemplate
{
return new PHPTemplatePageTemplate($this->createTitleTemplate());
}
}
/**
* Each distinct product type should have a separate interface. All variants of
* the product must follow the same interface.
*
* For instance, this Abstract Product interface describes the behavior of page
* title templates.
*/
interface TitleTemplate
{
public function getTemplateString(): string;
}
/**
* This Concrete Product provides Twig page title templates.
*/
class TwigTitleTemplate implements TitleTemplate
{
public function getTemplateString(): string
{
return "<h1>{{ title }}</h1>";
}
}
/**
* And this Concrete Product provides PHPTemplate page title templates.
*/
class PHPTemplateTitleTemplate implements TitleTemplate
{
public function getTemplateString(): string
{
return "<h1><?php print(\$title) ?></h1>";
}
}
/**
* This is another Abstract Product type, which describes whole page templates.
*/
interface PageTemplate
{
public function getTemplateString(): string;
}
/**
* The page template uses the title sub-template, so we have to provide the way
* to set it in the sub-template object. The abstract factory will link the page
* template with a title template of the same variant.
*/
abstract class BasePageTemplate implements PageTemplate
{
protected $titleTemplate;
public function __construct(TitleTemplate $titleTemplate)
{
$this->titleTemplate = $titleTemplate;
}
}
/**
* The Twig variant of the whole page templates.
*/
class TwigPageTemplate extends BasePageTemplate
{
public function getTemplateString(): string
{
$renderedTitle = $this->titleTemplate->getTemplateString();
return <<<EOF
<div class="page">
$renderedTitle
<article class="content">{{ content }}</article>
</div>
EOF;
}
}
/**
* The PHPTemplate variant of the whole page templates.
*/
class PHPTemplatePageTemplate extends BasePageTemplate
{
public function getTemplateString(): string
{
$renderedTitle = $this->titleTemplate->getTemplateString();
return <<<EOF
<div class="page">
$renderedTitle
<article class="content"><?php print(\$content) ?></article>
</div>
EOF;
}
}
/**
* The client code. Note that it accepts the Abstract Factory class as the
* parameter, which allows the client to work with any concrete factory type.
*/
function templateRenderer(TemplateFactory $factory)
{
$pageTemplate = $factory->createPageTemplate();
print($pageTemplate->getTemplateString());
// Here's how would you use the template further in real life:
// Twig::render($pageTemplate->getTemplateString(), [
// 'title' => $page->title,
// 'content' => $page->content, ]);
}
/**
* Now, in other parts of the app, the client code can accept factory objects of
* any type.
*/
print("Testing rendering with the Twig factory:\n");
templateRenderer(new TwigTemplateFactory());
print("\n\n");
print("Testing rendering with the PHPTemplate factory:\n");
templateRenderer(new PHPTemplateFactory());