Шаблоны проектирования: Factory Method
Коротко говоря Factory Method (Фабричный метод) — предоставляет подклассам некоторого класса фабрики (создателя) возможность создавать свойственные им конкретные продукты.
Когда применять? Если класс фабрика начинает обрастать логикой по созданию разного типа объектов (продуктов) и методы начинают содержать дублированные ветвления.
Пример: пусть у нас есть класс фабрики статей задача которого предоставлять по запросу коллекции статей заданного типа.
<?php
class ArticlesFactory
{
function getArticles($type)
{
switch($type)
{
case 'game':
$articles = new GameArticles(); break;
case 'video':
$articles = new VideoArticles(); break;
default:
throw new Exception('$type not defined');
}
return $articles;
}
function info($type)
{
switch($type)
{
case 'game':
$info = 'Статьи о играх'; break;
case 'video':
$info = 'Статьи о видео'; break;
default:
throw new Exception('$type not defined');
}
return $info;
}
}
interface Articles
{
function get($articleId);
}
class GameArticles implements Articles
{
function get($articleId)
{
return 'Статья об играх под #id '.$articleId;
}
}
class VideoArticles implements Articles
{
function get($articleId)
{
return 'Статья о видео под #id '.$articleId;
}
}
$articlesFactory = new ArticlesFactory();
echo $articlesFactory->info('video');
$articles = $articlesFactory->getArticles('video');
echo '<p>'.$articles->get(rand(1,100)).'</p>';
?>
после выполнения получим вывод примерно следующего содержания
Статьи о видео Статья о видео под #id 16
В коде намерено опущена проверка на ошибки для простоты примера. Главные участники здесь: ArticlesFactory — фабрика статей (в терминологии шаблона «создатель») и Articles — наборы статей (продукт).
На лицо несколько проблем:
- Дублирование кода (Добавление нового метода в фабрику (например подсчета количества статей заданного типа) приведет к очередному дубликату конструкции switch
- Тесная связь (Добавление нового типа статей приведет к правке всех методов фабрики)
Шаблон Factory Method предлагает для решения этих проблем создать специфичные фабрики для каждого из продуктов, т.е. фактически свеcти реализацию к полиморфизму.
<?php
interface ArticlesFactory
{
function getArticles();
function info();
}
class GameArticlesFactory implements ArticlesFactory
{
function getArticles()
{
return new GameArticles();
}
function info()
{
return 'Статьи о играх';
}
}
class VideoArticlesFactory implements ArticlesFactory
{
function getArticles()
{
return new VideoArticles();
}
function info()
{
return 'Статьи о видео';
}
}
interface Articles
{
function get($articleId);
}
class GameArticles implements Articles
{
function get($articleId)
{
return 'Статья об играх под #id '.$articleId;
}
}
class VideoArticles implements Articles
{
function get($articleId)
{
return 'Статья о видео под #id '.$articleId;
}
}
$videoArticlesFactory = new GameArticlesFactory();
echo $videoArticlesFactory->info();
$articles = $videoArticlesFactory->getArticles();
echo '<p>'.$articles->get(rand(1,100)).'</p>';
?>
Перечислим основные плюсы такого подхода:
- Клиентский код по созданию объектов стал более универсален и не привязан к конкретному продукту (типу статей), а оперирует общим интерфейсом ArticlesFactory
- Однозначная связь между параллельными иерархиям классов
Не обошлось и без минусов:
- Необходимо создавать новую фабрику под каждый новый тип статей
