Przede wszystkim interfejsy umożliwiają poprawną modularyzację aplikacji i ułatwiają modelowanie zależności pomiędzy obiektami. Są bardzo przydane przy projektowaniu modeli (domain models).
Pokaże to na prostym przykładzie wyszukiwarki produktów.
interface IProductSearch
{
/**
* Add product to search engine index
*
* @throws Exception
*/
public index(IProduct $product);
/*
* @return IProduct[] array of products hit by search
* @throws Exception
*/
public search
(array $product); }
Wszyscy wiemy jak wiele jest rozwiązań dotyczących wyszukiwarek, zatem:
class LuceneProductSearch implements IProductSearch
{
public index(IProduct $product)
{
// tutaj używamy np. Zend_Search_Lucene lub Java Bridge
}
public search
(array $product) {
// tutaj używamy np. Zend_Search_Lucene lub Java Bridge
}
}
// lub
class SphinxProductSearch implements IProductSearch
{
protected $pdo;
public function __construct(PDO $pdo)
{
$this->pdo = $pdo;
}
public index(IProduct $product)
{
// Prawdopodobnie nic nie robimy i korzystamy z automatycznego indeksowania Sphinxa
}
public search
(array $product) {
// wysyłamy odpowiednie zapytanie SQL do bazy
}
}
Użycie mogłoby wyglądać w ten sposób:
// Tworzymy i ustawiamy obiekt produktu jak i wyszukiwarki
$product->save($product_search);
// lub
$product->update($product_search);
W powyższym przykładzie
jak krowie na rowie wymodelowaliśmy zależność Produkt-Wyszukiwarka przy zapisywaniu i aktualizacji obiektu. Ta zależność może wyglądać tak:
// Teoretycznie można użyć ORM obiektu pod
// warunkiem, że implementuje interfejs IProduct
class DatabaseProduct implemnts IProduct
{
// etc
/**
* @return boolean true on success, false otherwise
*/
public function save(IProductSearch $product_search)
{
$this->pdo->beginTransaction();
try {
// W tym miejscu zapisujemy produkt do bazy danych
$this->setId($this->pdo->lastInsertId());
$product_search->index($this); // obiekt zapisany, teraz go zaindeksujmy w wyszukiwarce
$this->pdo->commit();
} catch(Exception $e) {
$this->pdo->rollBack();
return false;
}
return true;
}
}
Jeżeli do projektu jeszcze nie wybrano mechanizmu wyszukiwania, można by również zaimplementować pusty obiekt NoneProductSearch, który by zwyczajnie nic nie indeksował i wracał pustą tablicę przy wyszukiwaniu.
------------------------------------------------
Cały kłopot z programowaniem na interfejsach - jak również niechęć programistów - tkwi w instancjonowaniu obiektów. Trzeba się trochę nagimnastykować.
1. Operator
new$product_search = new LuceneProductSearch;
//lub
$product_search = new SphinxProductSearch($this->getContext()->getDatabase()->getDatabaseConnection());
Zwyczajne tworzenie obiektów już nie wystarcza. Potrzeba czegoś bardziej elastycznego, bo można skończyć (np. przy przesiadce z Lucene na Sphinxa) zmieniając nazwę obiektu wyszukiwarki w każdym miejscu, gdzie się jej używa
2. Wzorce kreacyjne i pochodne
$product_search = $this->getContext()->getProductSearch();
Już lepiej. Konfiguracja obiektu odbywa się w jednym miejscu, ale od ilości pisaniny (np. fabryka do każdego obiektu

) można szlak trafić.
// Dla uproszczenia załóżmy, że ta konkretna implementacja kontekstu jest
// również fabryką modeli ProductSearch
class myContext extends sfContext
{
// etc
/**
* @return IProductSearch
*/
public function getProductSearch()
{
return new SphinxProductSearch($this->getDatabaseConnection());
}
}
3. Bardziej kompleksowe rozwiązania.
Przykładem takowych są kontenery IoC. Całość tworzenia obiektów jest przekazywana do nich w formie np. wygodnej xmlowej konfiguracji.
$product_repository = $container>get('ProductRepository');
/* @var $product_repository IProductRepository */
$product = $productRepository->getProductById($request->getParameter('id'));
/* @var $product IProduct */
$product_search = $container>get('ProductSearch'); // Zwraca odpowiednio skonfigurowane LuceneProductSearch lub SphinxProductSearch
/* @var $product_search IProductSearch */
// W tym miejscu ustawiamy nowe wartości obiektu $product
$product->update($product_search); // et voila
// lub w zalezności, kto jak podchodzi do repozytoriów
$product_repository->save($product);