Slovník pojmů

Polymorfismus

Polymorfismus dovoluje, aby aplikační služba používala kartu, bankovní převod i testovací fake přes stejný významový kontrakt. Nejde jen o shodný název metody, ale o dodržení stejného pozorovatelného chování.

Stručná definice

Více typů, jeden skutečný kontrakt.

Volající pracuje s rozhraním nebo rodičovským typem. Konkrétní implementace se vybere při sestavení aplikace nebo za běhu a dynamické volání provede její metodu. Pokud implementace zachová kontrakt, přidání nové varianty nemění volající kód.

Polymorfismus může vzniknout přes dědičnost i přes interface. Dependency injection pouze dodá vybranou závislost; samo ještě nedokazuje, že kontrakt je malý, srozumitelný a behaviorálně dodržený.

Jaký problém řeší

Odděluje stabilní pravidlo od variantního detailu.

Hodí se tam, kde aplikace má několik skutečně zaměnitelných implementací se stejným významem.

  • platební gateway pro kartu, převod a test
  • úložiště s databázovou a paměťovou implementací
  • notifikace e-mailem, SMS nebo fake pro test
  • integrační adaptér pro několik providerů

Praktický příklad

Checkout závisí na PaymentGateway, ne na poskytovateli

CheckoutService zná jen kontrakt PaymentGateway. CardGateway a BankTransferGateway jej implementují pro skutečný provoz, FakePaymentGateway pak vrací předvídatelný výsledek v testu.

Nová brána se přidá bez změny CheckoutService jen tehdy, když vrací PaymentStart se stejným významem. Pokud potřebuje zcela jiné pokračování nebo zpřísní pravidla, je potřeba upravit kontrakt či oddělit use case, ne rozdíl skrýt za interface.

PHP

interface PaymentGateway
{
    public function start(Order $order): PaymentStart;
}

final class CardGateway implements PaymentGateway
{
    public function start(Order $order): PaymentStart
    {
        return PaymentStart::redirectToCardProvider();
    }
}

final class BankTransferGateway implements PaymentGateway
{
    public function start(Order $order): PaymentStart
    {
        return PaymentStart::waitingForTransfer();
    }
}

final class FakePaymentGateway implements PaymentGateway
{
    public function start(Order $order): PaymentStart
    {
        return PaymentStart::completedForTest();
    }
}

final class CheckoutService
{
    public function __construct(private PaymentGateway $gateway) {}

    public function beginPayment(Order $order): PaymentStart
    {
        return $this->gateway->start($order);
    }
}

Jak funguje

Výběr platební implementace přes kontrakt

Schéma ukazuje spolupráci přes interface, nikoli nutnost vytvořit interface pro každou třídu.

  1. CheckoutService Závisí na typu PaymentGateway.
  2. Společný kontrakt Určuje význam operace start a návratové hodnoty.
  3. Konkrétní gateway CardGateway nebo BankTransferGateway provede vlastní technické kroky.
  4. Testovací fake Dá testu předvídatelnou implementaci stejného kontraktu.

Hlavní části a varianty

Kontrakt je důležitější než počet tříd.

Polymorfní návrh má přinést skutečnou zaměnitelnost, ne jen další vrstvu názvů.

Rozhraní

Vyjadřuje operace a jejich význam, na kterém mohou záviset klienti.

Rodičovský typ

Může poskytnout polymorfní hranici přes dědičnost, pokud potomci zachovají kontrakt.

Substituce

Jedna implementace nahradí druhou bez změny správnosti volajícího kódu.

Testovací implementace

Fake nebo stub dovolí ověřit službu bez skutečné externí platební brány.

Omezení a časté chyby

Abstrakce musí odpovídat skutečné variabilitě.

Kde pomáhá

  • přidat novou kompatibilní bránu bez změny klienta
  • testovat službu s fake implementací
  • oddělit business pravidlo od technického providera

Rizika

  • interface pro každou třídu bez druhého klienta nebo kontraktu
  • implementace s rozdílným behaviorálním významem
  • mnoho malých implementací zhoršujících orientaci
  • představa, že DI samo zajistí dobrý návrh

Praktické použití

Zaměnitelnost je třeba dokázat scénářem.

Rozhraní má vzniknout tehdy, když existuje skutečný stabilní kontrakt, více implementací nebo rozumná potřeba izolovat hranici v testu. Ne každá třída potřebuje vlastní interface.

Pro každou implementaci je vhodné ověřit stejný kontraktní scénář. Jinak aplikace jen přesune podmínky z klienta do implementací a ztratí předvídatelnost.

Polymorfismus není dědičnost ani přetěžování metod podle signatur a není totéž co generické typy. Dědičnost může nabídnout společný rodičovský typ, rozhraní však často stačí bez něj. Pro malou stabilní volbu může být čitelnější obyčejná podmínka; abstrakci má smysl přidat až pro skutečnou zaměnitelnost.

Kontraktní test pro PaymentGateway může například ověřit, že každá implementace vrátí srozumitelný stav zahájení platby a neprovede platbu dvakrát při stejném požadavku. Fake pak testuje CheckoutService bez sítě, ale nesmí předstírat chování, které skutečná brána neumí. Takové scénáře chrání substituci lépe než samotná shoda názvů metod.

Volající služba proto nesmí podle konkrétní třídy obcházet význam rozhraní a doplňovat vlastní výjimky pro jednu gateway. Když takové větvení vznikne, je potřeba zjistit, zda kontrakt neobsahuje dvě odlišné odpovědnosti. Někdy je správným řešením rozdělit use case nebo ponechat jednoduchou větev, ne za každou cenu zachraňovat příliš obecné rozhraní.

Polymorfismus je užitečný hlavně na stabilní hranici, kde se technický detail může měnit častěji než business pravidlo. CheckoutService tak může zůstat soustředěný na zahájení platby, zatímco konkrétní gateway řeší komunikaci s poskytovatelem. Pokud se každá implementace musí ověřovat řetězcem if podle své třídy, společný kontrakt pravděpodobně nepopisuje skutečné společné chování.

Kontrola změny

Ověřte chování každé implementace stejnou otázkou.

Kontrakt nesmí být jen seznam metod; musí popsat, co klient může očekávat.

  • popsat význam vstupu a výstupu rozhraní
  • testovat alternativní implementace společným scénářem
  • nepřidávat interface bez skutečné hranice
  • ponechat jednoduchý if tam, kde je čitelnější

Časté otázky

Polymorfismus v praxi

Je pro polymorfismus nutná dědičnost?

Ne. Často stačí interface, který implementují nezávislé třídy.

Musí mít každá třída interface?

Ne. Interface má hodnotu při skutečném kontraktu nebo zaměnitelnosti, ne jako povinný doplněk.

Co je substituce?

Možnost nahradit jednu implementaci jinou bez porušení očekávání klienta.

Kdy je if lepší?

Když je rozhodnutí malé, stabilní a abstrakční hierarchie by přidala více složitosti než jasnosti.

Osobní zkušenost

Kontrakty mají chránit změnu, ale zůstat malé a pravdivé.

V architektuře API a PHP aplikací používám polymorfismus tam, kde skutečně oddělí stabilní pravidlo od proměnlivé integrace.

Zavolejte mi

Zavolám vám následující pracovní den mezi 9:00 a 17:00.

Můžete mi také zavolat rovnou.

+420 605 181 728

Nechte mi telefonní číslo a pošlete žádost o zpětné zavolání.

Odesláním souhlasíte se zpracováním údajů pro vyřízení žádosti.