Фабричный метод - PHP

Рассмотрим простой пример Фабричного метода - с минимальным набором сущностей:

/* Абстрактный "создатель" - 
в данном примере выполнен в виде интерфейса  -
но можно использовать и класс - в том числе - абстрактный класс */
interface CarFactory {
// вот этот метод как раз и называют фабричным
    public function makeCar(); // фабр. метод
}
 
/*Абстрактный "продукт"*/
interface Car {
// здесь может быть какой угодно набор методов
    public function getType(); 
}
 
/* Далее будут приведены конкретные реализации
 абстракций создателя и продукта */ 
 
/*  конкретный "создатель" - фабрика кузовов типа "сидан" */
class SedanFactory implements CarFactory {
/* следующий метод - это как раз 
конкретная реалзизация фабричного метода makeCar()*/
    public function makeCar() {  // создаём кузов 
        return new Sedan();
    }
}
 
/*  конкретный продукт   - напр. кузов машины типа "сидан"*/
class Sedan implements Car {
    public function getType() { // например возврат конкретного типа
        return 'Sedan';
    }
}
 
/* Далее - клиенский код -
то есть пример эксплуатации созданных выше двух иерархий классов*/
$factory = new SedanFactory(); // создаём фабрику
$car = $factory->makeCar(); // вызываем фабричный метод и получаем машину (кузов)
print $car->getType(); //  начинаем работать с полученным объектом

Ещё раз повторюсь - что приведённый выше пример является минимальным - он строго говоря даже не демонстрирует удобство использования паттерна "Фабричный метод"-

Ведь на самом деле вся фишка в клиенском коде. - дело в том, что оказывается удобным создать для клиента универсальную функцию для работы с различными создателями (фабриками) -и параметризовать её уже объектом-фабрикой (создателем)/
Например так:

<?

/*пример клиенского кода для фабричного метода 
который НЕ ЗАВИСИТ от того какую именно конкретную фабрику
(конкретного создателя) передадут ему*/
class Project // некий охватывающий контекст класс
{
	public $factory = "";
	
	/*получаем конкретную фабрику в конструкторе - 
	например от меню когда пользователь выбрал нужный тип машины*/
	__construct($factory){ // получаем
		$this->factory =  $factory; // сохраняем эту фабрику
	}

	function GetProduct()
	{
// используем фабрику полунную при создании контекста
		$car = $this->factory->makeCar();
		return $car;
	}	
		
	/* метод ниже будем рассматривать как клиентский код*/
	function GetCarName() // "клиент"
	{
		$car = $this->GetProduct($this->factory);
		print $car->getType(); // выводим тип
	}

/*----какие-то ещё методы-----*/
}


Теперь (когда мы определили класс Project) "непосредственно клиенский" код может выглядеть так:

/* Далее - клиенский код -
то есть пример эксплуатации созданных выше двух иерархий классов*/
if ($menuitem == 'sedan') // какое-то условие (например пользователь выбрал пункт меню)
{
  $factory = new SedanFactory(); // создаём фабрику
}
/* 
выше сделан выбор, а далее
идёт абсолютно "гибкий" код клиента
*/
$project = new Project($factory); // этот класс не знает заранее "с кем" будет работать -
// но при этом мы ("клиент") также не знаем какую именно машину получим в  $car
  
$car = $project->GetProduct(); // вызываем фабричный метод и получаем машину (кузов)
print $car->getType(); //  начинаем работать с полученным объектом 

Последний приведённый фрагмент кода вполне можно считать демонстрацией использования паттерна фабричный метод, ведь данный пример:

  1. использует для порождения конкретной машины (продукта) конкретную фабрику (класс-создатель) - кстати именно это и есть формальный признак использования шаблона фабричный метод
  2. приведённый фрагмент во второй своей части "гибок" - то есть при добавлении новых классов вторую часть кода редактировать вообще не придётся - таким образом данный паттерн облегчает жизнь разработчика

Key Words for FKN + antitotal forum (CS VSU):