PHP je věda

Objektově orientované programování nejen v PHP

Polymorfismus

Výhody dědičnosti zde však ještě nekončí! Ve složitějších případech je dědičnost dobrý pomocník s využitím polymorfismu. K vysvětlení polymorfismu si můžeme představit následující: Náš program ví, že na roznranní jsou uživatelé s id 1 až 10 a tyto uživatele chceme stáhnout a uložit do databáze.

<?php
class DownloadedUserSavingService {
 
    /**
     * @var UserDownloader
     */
    protected $userDownloader;
 
    /**
     * @var UserRepository
     */
    protected $repository;
 
    /**
     * @param UserDownloader $userDownloader
     * @param UserRepository $repository
     */
    public function __consruct(UserDownloader $userDownloader, UserRepository $repository) {
        $this->userDownloader = $userDownloader;
        $this->repository = $repository;
    }
 
    /**
     * @return bool
     */
    public function downloadAndSaveUsers() {
        for ($i = 1; $i < 10; $i++) {
            $user = $this->userDownloader->downloadUser($i);
            $this->repository->save($user);
        }
        return true;
    }
}

Podíváme-li se na konstruktor, všimneme si, že typ předaného downloaderu je UserDownloader, tedy typ abstraktní třídy a né konkrétní implementace. Právě tento příklad je využití polymorfismu. Třída DownloadedUserSavingService se může spolehnout, že předaná instance je typu UserDownloader a že na ní může zavolat metodu downloadUser, která vrací typ User. Tento typ – ať už v něm jsou jakákoliv data umí zpracovat metoda save třídy UserRepository. Co se týče třídy UserRepository, předpokládejme pouze, že třída musí implementovat následující interface – tedy metodu save a ostatní metody, které jsme již ve dřívějších případech používali. Metoda save uloží uživatelská data do databáze. Jakým způsobem – o to se nemusíme starat.

<?php
Interface UserRepository {
 
    /**
     * @param User $user
     *
     * @return User
     */
    public function save(User $user);
 
    /**
     * @param string $name
     *
     * @return User
     */
    public function findByName($name);
 
    /**
     * @param string $name
     * @param string $password
     *
     * @return User
     */
    public function findByIdAndPassword($name, $password);
}

Jako konkrétní implementaci interface si můžeme představit například třídu UserMySqlRepository, která pracuje – jak již z názvu vyplývá – s databází mysql.

<?php
 
class UserMySqlRepository implements UserRepository {
 
    /**
     * @param User $user
     *
     * @return User
     */
    public function save(User $user) {
        // implementovat logiku ukladani vlastnosti objektu user do databaze
    }
 
    /**
     * @param string $name
     *
     * @return User
     */
    public function findByName($name) {
        // implementovat logiku vyhledavani uzivatele podle jmena
        // vytvorit z nalezenych dat objekt typu user
    }
 
    /**
     * @param string $name
     * @param string $password
     *
     * @return User
     */
    public function findByIdAndPassword($name, $password) {
        // vyhledat uzivatele podle jmena a hesla
        // vytvorit z nalezenych dat objekt typu user
    }
 
}

Jak to potom vypadá v praxi a jak lze polymorfismus využít? Lze napsat například jednoduchý script, který lze spustit z příkazové řádky.

<?php
$type = null;
 
if (isset($argv[1])) {
    $type = $argv[1];
}
 
switch ($type) {
    case "json":
        $downloader = new JsonDownloader();
        break;
    case "xml":
        $downloader = new XmlDownloader();
        break;
    default:
        die("Invalid type $type");
}
 
$repository               = new UserMySqlRepository();
$downloadAndSavingService = new DownloadedUserSavingService($downloader, $repository);
 
$downloadAndSavingService->downloadAndSaveUsers();

Volání scriptu z příkazové řádky potom vypadá následovně:

// pro stahovani z xml rozhranni
php DownloaderScript.php xml

// pro stahovani z json roznranni
php DownloaderScript.php xml