Most egy másik Creational Pattern kerül bemutatásra: a Factory Method.
Ahogy a neve is utalja - valaminek a legyártására lett kitalálva - OOP világában az objektumok gyártása a cél.
Mikor alkalmazzuk: ha előre nem tudjuk vagy nem is akarjuk kikötni azt, hogy mely osztály példányára lesz szükségünk - azaz növeljük a kód és az üzleti logika közötti függetlenségét.
Tegyük fel, hogy rendelkezünk az alábbi osztályokkal:
class ProductClass{ ... }
class CategoryClass{ ... }
class UserClass{ ... }
class OtherClass{ ... }
Tegyük fel azt is, hogy az arra vonatkozó információt, hogy milyen osztály példányára van szükségünk egy külső forrásból kapjuk ( config, adatbázis, etc. ). Így valamelyest felkészülhetünk a kérés feldolgozására a következő módon:
/*
* Az alábbi változó tartalmazza azt az információt
* amely a példányosítandó osztály azonosításához alkalmas
*/
$classIdentifier;
switch($classIdentifier){
case 'Product': return new ProductClass();
break;
case 'Category': return new CategoryClass();
break;
case 'User': return new UserClass();
break;
case 'Other': return new OtherClass();
break;
}
Ez eddig szép meg jó, de mi van akkor, ha nem négy osztályról van szó hanem mondjuk 100 -ról? Elég érdekes lenne megírni 100 switch-case elágaztatást vagy if-else ágat...
A megoldás: szükségünk van egy (statikus) egységre, amely paramétertől függően gyártaná le nekünk az osztálypéldányokat. Mivel a switch/case és az if/else megoldás kiesik a fenti példa miatt, a következőt alkalmazzuk:
/**
* @author 6aIL6ec
*
* Factory Method működését szemléltető osztály
*
*/
class Factory{
/**
* Ez a gyártó metódus
*/
public static function getOneByIdentifier($idf){
/*
* Dinamikusan összerakom az osztály nevét
* egyszerű konkatenációval
* ezért célszerű olyan egységes osztályneveket és
* külső azonosító stringeket
* alkalmazni, amelyekből valamely egyszerű
* stringművelettel megkapjuk a keresendő osztály
* nevét
*/
$className = $idf . 'Class';
/* $idf = 'Product' esetén a $className
* értéke 'ProductClass' lesz
*/
/* A PHP meg szépen kiértékeli a $className szöveges
* értékét, így $class = 'ProductClass' esetén a
* ProductClass osztály konstruktorát fogja hívni a
* következő sor
*/
return new $className();
}
}
Amennyiben a paraméterként átadott osztálynév nem létezik (nincs behúzva) és példányosításra kerülne sor, kapunk egy Fatal error -t. Ezért célszerű megvizsgálni azt is, hogy az adott osztálynévvel létezik-e osztálydeklaráció:
/**
* @author 6aIL6ec
*
* Factory Method működését szemléltető osztály
*
*/
class Factory{
/**
* Ez a gyártó metódus
*/
public static function getOneByIdentifier($idf){
$className = $idf . 'Class';
if(class_exists($className)){
return new $className();
}else{
trigger_error('A(z) ' . $className .
' osztály nem található!',
E_USER_ERROR);
}
}
}
Így az 1. példának megadott switch/case helyett a következő alternatívát kaptunk:
/*
* Az alábbi változó tartalmazza azt az információt
* amely a példányosítandó osztály azonosításához alkalmas
*/
$classIdentifier;
$newInstance = Factory::getOneByType($classIdentifier);
Folyt. köv.