I objektorienterad programmering är polymorfism ett kraftfullt och grundläggande verktyg. Det kan användas för att skapa ett mer organiskt flöde i din ansökan. Denna handledning kommer att beskriva det allmänna begreppet polymorfism, och hur det enkelt kan distribueras i PHP.
Polymorfism är ett långt ord för ett mycket enkelt koncept.
Polymorfism beskriver ett mönster i objektorienterad programmering där klasser har olika funktioner medan de delar ett gemensamt gränssnitt.
Skönheten i polymorfismen är att koden som arbetar med olika klasser inte behöver veta vilken klass den använder eftersom de alla används på samma sätt.
En riktig världsanalog för polymorfismen är en knapp. Alla vet hur man använder en knapp: du applicerar bara tryck på det. Vad en knapp "gör" beror emellertid på vad det är kopplat till och det sammanhang där det används - men resultatet påverkar inte hur det används. Om din chef berättar att du trycker på en knapp har du redan all information som behövs för att utföra uppgiften.
I programmeringsvärlden används polymorfism för att göra applikationerna mer modulära och utvidgbara. Istället för stökiga villkorliga uttalanden som beskriver olika handlingsalternativ skapar du utbytbara objekt som du väljer utifrån dina behov. Det är det grundläggande målet för polymorfism.
En integrerad del av polymorfismen är det gemensamma gränssnittet. Det finns två sätt att definiera ett gränssnitt i PHP: gränssnitt och abstrakta klasser. Båda har sina användningsområden, och du kan mixa och matcha dem som passar dig i din klasshierarki.
Ett gränssnitt liknar en klass förutom att det inte kan innehålla kod. Ett gränssnitt kan definiera metodnamn och argument, men inte innehållet i metoderna. Alla klasser som implementerar ett gränssnitt måste implementera alla metoder som definieras av gränssnittet. En klass kan implementera flera gränssnitt.
Ett gränssnitt är deklarerat med hjälp av "gränssnitt
'nyckelord:
gränssnitt MyInterface // methods
och är knuten till en klass med "redskap
'nyckelord (flera gränssnitt kan implementeras genom att lista dem separerade med kommatecken):
klass MyClass implementerar MyInterface // methods
Metoder kan definieras i gränssnittet precis som i en klass, utom utan kroppen (delen mellan axlarna):
gränssnitt MyInterface public function doThis (); allmän funktion doThat (); public function setName ($ name);
Alla metoder som definieras här måste inkluderas i någon implementeringsklass exakt som beskrivet. (läs koden kommentarer nedan)
// Giltig klass MyClass implementerar MyInterface protected $ name; public function doThis () // kod som gör det här public function doThat () // kod som gör det allmän funktion setName ($ name) $ this-> name = $ name; // INVALID-klass MyClass implementerar MyInterface // missing doThis ()! privat funktion doThat () // det borde vara offentligt! public function setName () // saknar namnet argument!
En abstrakt klass är en blandning mellan ett gränssnitt och en klass. Det kan definiera funktionalitet såväl som gränssnitt (i form av abstrakta metoder). Klasser som omfattar en abstrakt klass måste genomföra alla abstrakta metoder som definieras i abstraktklassen.
En abstrakt klass förklaras på samma sätt som klasser med tillägg av "abstrakt
'nyckelord:
abstrakt klass MyAbstract // methods
och är knuten till en klass med "sträcker
'nyckelord:
klass MyClass utökar MyAbstract // klassmetoder
Regelbundna metoder kan definieras i en abstrakt klass, precis som i en vanlig klass, samt några abstrakta metoder (med hjälp av "abstrakt
'nyckelord). Abstrakta metoder beter sig precis som metoder definierade i ett gränssnitt, och måste genomföras exakt som definieras genom att utöka klasserna.
abstrakt klass MyAbstract public $ name; public function doThis () // gör detta abstrakt offentlig funktion doThat (); abstrakt offentlig funktion setName ($ namn);
Låt oss föreställa oss att du har en Artikel
klass som ansvarar för att hantera artiklar på din webbplats. Den innehåller information om en artikel, inklusive titel, författare, datum och kategori. Så här ser det ut:
klass poly_base_Article public $ title; offentlig $ författare; offentlig $ date; offentlig $ kategori; offentlig funktion __construct ($ title, $ author, $ date, $ category = 0) $ this-> title = $ title; $ this-> author = $ author; $ this-> date = $ date; $ this-> category = $ category;
Notera: Exempelklasserna i denna handledning använder namngivningskonventionen för "package_component_Class." Detta är ett vanligt sätt att skilja klasser i virtuella namnområden för att undvika namnkollisioner.
Nu vill du lägga till en metod för att mata ut informationen i olika format, till exempel XML och JSON. Du kan bli frestad att göra något så här:
klass poly_base_Article // ... public function write ($ typ) $ ret = "; switch ($ type) case 'XML': $ ret = ''; $ ret. = ' '; ha sönder; fallet "JSON": $ array = array ('article' => $ obj); $ ret = json_encode ($ array); ha sönder; returnera $ ret;'. $ obj-> titel. ' '; $ ret. = ''. $ obj-> författare. ' '; $ ret. = ''. $ obj-> datum. ' '; $ ret. = ''. $ obj-> kategori. ' '; $ ret. = '
Det här är typ av en ful lösning, men det fungerar - för nu. Fråga dig själv vad som händer i framtiden, men när vi vill lägga till fler format? Du kan fortsätta redigera klassen, lägga till fler och fler fall, men nu sparar du bara din klass.
En viktig princip för OOP är att en klass bör göra en sak, och det borde göra det bra.
Med detta i åtanke bör villkorliga uttalanden vara en röd flagg som indikerar att din klass försöker göra för många olika saker. Det är här polymorfismen kommer in.
I vårt exempel är det klart att det finns två uppgifter som presenteras: hantera artiklar och formatera deras data. I denna handledning reflekterar vi vår formateringskod i en ny uppsättning klasser och upptäcker hur enkelt det är att använda polymorfismen.
Det första vi bör göra är att definiera gränssnittet. Det är viktigt att tänka hårt om ditt gränssnitt, eftersom eventuella förändringar i det kan kräva ändringar av anropskoden. I vårt exempel använder vi ett enkelt gränssnitt för att definiera vår enda metod:
gränssnitt poly_writer_Writer public function write (poly_base_Article $ obj);
Det är så enkelt; Vi har definierat en allmänhet skriva()
metod som accepterar ett artikelobjekt som ett argument. Alla klasser som implementerar Writer-gränssnittet kommer säkert att ha den här metoden.
Tips: Om du vill begränsa vilken typ av argument som kan överföras till dina funktioner och metoder, kan du använda typtips som vi har gjort i skriva()
metod; det accepterar endast objekt av typen poly_base_Article
. Tyvärr kan inte returtyphinting stödjas i nuvarande versioner av PHP, så det är upp till dig att ta hand om returvärden.
Med ditt gränssnitt definierat är det dags att skapa klasserna som faktiskt gör saker. I vårt exempel har vi två format som vi vill producera. Således har vi två Writer-klasser: XMLWriter och JSONWriter. Det är upp till dessa att extrahera data från det överförda artikelobjektet och formatera informationen.
Så här ser XMLWriter ut:
klass poly_writer_XMLWriter implementerar poly_writer_Writer public function write (poly_base_Article $ obj) $ ret = ''; $ ret. = ' '; returnera $ ret;'. $ obj-> titel. ' '; $ ret. = ''. $ obj-> författare. ' '; $ ret. = ''. $ obj-> datum. ' '; $ ret. = ''. $ obj-> kategori. ' '; $ ret. = '
Som du kan se från klassdeklarationen använder vi redskap
sökord för att implementera vårt gränssnitt. De skriva()
Metoden innehåller funktionalitet som är specifik för formatering av XML.
Nu är vår JSONWriter-klass:
klass poly_writer_JSONWriter implementerar poly_writer_Writer public function write (poly_base_Article $ obj) $ array = array ('article' => $ obj); returnera json_encode ($ array);
All vår kod som är specifik för varje format finns nu i enskilda klasser. Dessa klasser har ensam ansvaret för att hantera ett visst format, och inget annat. Ingen annan del av din ansökan behöver bry sig om hur dessa fungerar för att kunna använda det tack vare vårt gränssnitt.
Med våra nya klasser definierade är det dags att återfå vår artikel klass. All kod som levde i originalet skriva()
Metoden har varit föremål för vår nya uppsättning klasser. Allt vårt sätt att göra nu är att använda de nya klasserna, så här:
klass poly_base_Article // ... public function write (poly_writer_Writer $ writer) return $ writer-> write ($ this);
All denna metod accepterar nu ett objekt av Writer-klassen (det är vilken klass som implementerar Writer-gränssnittet), ring dess skriva()
metod som passerar sig själv ($ detta
) som argument och vidarebefordra dess returvärde direkt till klientkoden. Det behöver inte längre oroa sig för detaljerna i formateringsdata, och det kan fokusera på sin huvuduppgift.
Du kanske undrar var du får ett Writer-objekt till att börja med, eftersom du måste skicka en till den här metoden. Det är upp till dig, och det finns många strategier. Du kan till exempel använda en fabriksklass för att få tag i förfrågningsdata och skapa ett objekt:
klass poly_base_Factory offentliga statiska funktion getWriter () // grab request variable $ format = $ _REQUEST ['format']; // konstruera vårt klassnamn och kontrollera dess existens $ class = 'poly_writer_'. $ format. 'Författare'; om (class_exists ($ class)) // returnera ett nytt Writer-objekt returnera nytt $ class (); // annars misslyckas vi med att kasta ny Undantag ('Otillräckligt format');
Som sagt, det finns många andra strategier att använda beroende på dina krav. I det här exemplet väljer en begäran variabel vilket format som ska användas. Den konstruerar ett klassnamn från begäran variabel, kontrollerar om det existerar, och returnerar sedan ett nytt Writer-objekt. Om ingen finns under det namnet kastas ett undantag för att låta kundkoden räkna ut vad som ska göras.
Med allt på plats, här är hur vår klientkod skulle lägga allt ihop:
$ article = new poly_base_Article ('Polymorphism', 'Steve', tid (), 0); försök $ writer = poly_base_Factory :: getWriter (); fångst (Undantag $ e) $ writer = ny poly_writer_XMLWriter (); echo $ article-> write ($ writer);
Först skapade vi ett exempel Artikelobjekt att arbeta med. Då försöker vi få ett Writer-objekt från fabriken som faller tillbaka till en standard (XMLWriter) om ett undantag kastas. Slutligen skickar vi Writer-objektet till vår artikel skriva()
metod, utskrift av resultatet.
I denna handledning har jag försett dig med en introduktion till polymorfism och en förklaring av gränssnitt i PHP. Jag hoppas du inser att jag bara har visat dig ett potentiellt användningsfall för polymorfism. Det finns många, många fler applikationer. Polymorfism är ett elegant sätt att fly från fula villkorade uttalanden i din OOP-kod. Det följer principen om att hålla komponenterna separata och är en integrerad del av många designmönster. Om du har några frågor, tveka inte att fråga i kommentarerna!