Înțelegerea și aplicarea polimorfismului în PHP

În programarea orientată obiect, polimorfismul este un instrument puternic și fundamental. Acesta poate fi folosit pentru a crea un flux mai organic în aplicația dvs. Acest tutorial va descrie conceptul general de polimorfism și modul în care poate fi ușor implementat în PHP.


Ce este polimorfismul??

Polimorfismul este un cuvânt lung pentru un concept foarte simplu.

Polimorfismul descrie un model în programarea orientată obiect în care clasele au funcționalități diferite în timp ce partajează o interfață comună.

Frumusețea polimorfismului este că codul care lucrează cu diferitele clase nu are nevoie să știe ce clasă utilizează, deoarece toate sunt utilizate în același mod.

O analogie a lumii reale pentru polimorfism este un buton. Toată lumea știe cum să folosească un buton: pur și simplu aplicați presiune asupra acestuia. Ce buton "face", totuși, depinde de ce este conectat și de contextul în care este utilizat - dar rezultatul nu afectează modul în care este utilizat. Dacă șeful dvs. vă spune să apăsați un buton, aveți deja toate informațiile necesare pentru a efectua sarcina.

În lumea programării, polimorfismul este folosit pentru a face aplicațiile mai modulare și extensibile. În loc de declarații condiționate murdare care descriu diferite cursuri de acțiune, creați obiecte interschimbabile pe care le selectați în funcție de nevoile dvs. Acesta este obiectivul fundamental al polimorfismului.


interfeţe

O parte integrantă a polimorfismului este interfața comună. Există două modalități de a defini o interfață în PHP: interfețe și clase abstracte. Ambele au utilizările lor și le puteți amesteca și le potrivi cum vă vedeți în ordinea ierarhiei de clasă.

Interfață

O interfață este similară unei clase, cu excepția faptului că nu poate conține cod. O interfață poate defini nume de metode și argumente, dar nu conținutul metodelor. Orice clase care implementează o interfață trebuie sa implementa toate metodele definite de interfață. O clasă poate implementa mai multe interfețe.

O interfață este declarată utilizând "interfațăCuvinte cheie:

interfață MyInterface // methods

și este atașat la o clasă folosind "ustensile"cuvânt cheie (mai multe interfețe pot fi implementate prin listare separată prin virgule):

clasa MyClass implementează MyInterface // methods

Metodele pot fi definite în interfață la fel ca într-o clasă, cu excepția cazului în care nu există corpul (partea dintre bretele):

interfața MyInterface funcția publică doThis (); funcția publică doThat (); funcția publică setName ($ name); 

Toate metodele definite aici trebuie să fie incluse în toate clasele de implementare exact așa cum este descris. (citiți comentariile de mai jos)

// clasa VALID MyClass implementează MyInterface protejat $ name; funcția publică doThis () // cod care face aceasta funcția publică doThat () // codul care face că funcția publică setName ($ name) $ this-> name = $ name;  // Clasa INVALID MyClass implementează MyInterface // missing toThis ()! funcția privată doThat () // aceasta ar trebui să fie publică!  funcția publică setName () // lipsește argumentul de nume! 

Clasa abstractă

O clasă abstractă este o combinație între o interfață și o clasă. Poate defini funcționalitatea și interfața (sub formă de metode abstracte). Clasele care extind o clasă abstractă trebuie sa să implementeze toate metodele abstracte definite în clasa abstractă.

O clasă abstractă este declarată la fel ca clasele cu adăugarea clasei "abstractCuvinte cheie:

clasă abstractă MyAbstract // methods

și este atașat la o clasă folosind "extindeCuvinte cheie:

clasa MyClass extinde MyAbstract // metode de clasă

Metodele regulate pot fi definite într-o clasă abstractă la fel ca într-o clasă obișnuită, precum și orice metodă abstractă (folosind metoda "abstract"cuvânt cheie). Metodele abstract se comportă exact ca metodele definite într-o interfață și trebuie implementate exact așa cum este definită prin extinderea clasei.

clasa abstractă MyAbstract public $ name; funcția publică doThis () // do this funcția publică abstractă doThat (); funcția publică abstractă setName ($ name); 

Pasul 1: Identificați problema

Să ne imaginăm că aveți un Articol clasa care este responsabilă pentru gestionarea articolelor de pe site-ul dvs. web. Acesta conține informații despre un articol, inclusiv titlul, autorul, data și categoria. Iată cum arată:

clasa poly_base_Article public $ title; public autor $; data publicului; categorie publică; funcția publică __construct ($ titlu, $ autor, $ date, $ category = 0) $ this-> title = $ title; $ this-> author = $ autor; $ this-> date = $ date; $ this-> category = $ category; 

Notă: Exemplele de clase din acest tutorial utilizează convenția de denumire a "package_component_Class". Acesta este un mod obișnuit de a separa clasele în spații de nume virtuale pentru a evita coliziunile numelui.

Acum doriți să adăugați o metodă de ieșire a informațiilor în diferite formate, cum ar fi XML și JSON. Ați putea fi tentat să faceți ceva de genul:

clasa poly_base_Article // ... funcția publică scrie ($ type) $ ret = "; comutator ($ type) caz 'XML': $ ret = '
„; $ ret. = '". $ obj-> titlu. '„; $ ret. = '". $ obj-> autor. '„; $ ret. = '". $ obj-> data. '„; $ ret. = '". $ obj->. '„; $ ret. = '
„; pauză; cazul "JSON": $ array = array ('articolul' => $ obj); $ net = json_encode ($ array); pauză; return $ ret;

Aceasta este o soluție urâtă, dar funcționează - deocamdată. Întrebați-vă ce se întâmplă în viitor, totuși, când vrem să adăugăm mai multe formate? Poți continua să editezi clasa, adăugând tot mai multe cazuri, dar acum îți diluezi clasa.

Un principiu important al OOP este acela că o clasă ar trebui să facă un lucru și ar trebui să o facă bine.

Având în vedere acest lucru, afirmațiile condiționale ar trebui să fie un steag roșu care să indice că clasa voastră încearcă să facă prea multe lucruri diferite. Aici ajunge polimorfismul.

În exemplul nostru, este clar că există două sarcini prezentate: gestionarea articolelor și formatarea datelor. În acest tutorial, vom refactor codul nostru de formatare într-un nou set de clase și vom descoperi cât de ușor este de a folosi polimorfism.


Pasul 2: Definiți-vă interfața

Primul lucru pe care ar trebui să-l facem este să definim interfața. Este important să vă gândiți mult la interfața dvs., deoarece orice modificare a acesteia poate necesita modificări ale codului de apel. În exemplul nostru, vom folosi o interfață simplă pentru a defini metoda noastră:

interfața poli_writer_Writer scrierea funcției publice (poly_base_Article $ obj); 

Este atat de simplu; am definit un public scrie() care acceptă un obiect de articol ca argument. Orice clase care implementează interfața Writer vor avea siguranța că vor avea această metodă.

Bacsis: Dacă doriți să restricționați tipul de argumente care pot fi transmise funcțiilor și metodelor dvs., puteți utiliza sugestii de tip, așa cum am făcut în scrie() metodă; acceptă doar obiecte de tip poly_base_Article. Din păcate, sugestia de tip întoarcere nu este acceptată în versiunile curente de PHP, așa că depinde de tine să ai grijă de valorile returnate.


Pasul 3: Creați-vă implementarea

Cu interfața dvs. definită, este timpul să creați clasele care efectiv fac lucruri. În exemplul nostru, avem două formate pe care dorim să le transmitem. Astfel, avem două clase Writer: XMLWriter și JSONWriter. Depinde de acestea să extragă datele din obiectul Articol trecut și să formateze informațiile.

Iată ce arată XMLWriter:

clasa poly_writer_XMLWriter implementează poli_writer_Writer public function write (pol_base_Article $ obj) $ ret = '
„; $ ret. = '". $ obj-> titlu. '„; $ ret. = '". $ obj-> autor. '„; $ ret. = '". $ obj-> data. '„; $ ret. = '". $ obj->. '„; $ ret. = '
„; retur;

După cum puteți vedea din declarația de clasă, vom folosi ustensile cuvânt cheie pentru a implementa interfața noastră. scrie() metoda conține funcționalitate specifică formatării XML.

Acum este clasa noastră JSONWriter:

clasa poly_writer_JSONWriter implementează poli_writer_Writer public function write (pol_base_Article $ obj) $ array = array ('articolul' => $ obj); returnează json_encode ($ array); 

Toate codurile noastre specifice pentru fiecare format sunt acum cuprinse în clase individuale. Aceste clase au fiecare responsabilitatea exclusivă de a gestiona un anumit format, și nimic altceva. Nici o altă parte din aplicația dvs. nu trebuie să aibă grijă de modul în care acestea funcționează pentru ao folosi, datorită interfeței noastre.


Pasul 4: Utilizați implementarea

Odată cu definirea noilor noastre clase, este timpul să revedem clasa noastră de articole. Tot codul care a trăit în original scrie() metoda a fost inclusă în noul nostru set de clase. Toată metoda noastră trebuie să facem acum este să folosim noile clase, cum ar fi:

clasa poly_base_Article // ... funcția publică scrie (poly_writer_Writer $ writer) return $ writer-> write ($ this); 

Totuși, această metodă acceptă acum un obiect al clasei Writer (care este orice clasă care implementează interfața Writer), apelează la scrie() metodă, trecându-se ($ this) ca argument, apoi transmite valoarea de retur direct la codul clientului. Nu mai trebuie să-și facă griji cu privire la detaliile formării datelor și se poate concentra asupra sarcinii sale principale.

Obținerea unui scriitor

S-ar putea să te întrebi de unde începi să începi un obiect Writer, din moment ce trebuie să treci unul la această metodă. Depinde de tine, și există multe strategii. De exemplu, este posibil să utilizați o clasă din fabrică pentru a prelua datele de solicitare și pentru a crea un obiect:

clasa poly_base_Factory funcția publică statică getWriter () // apuca variabila cererii $ format = $ _REQUEST ['format']; // construiți numele clasei și verificați existența ei $ class = 'poly_writer_'. $ format. 'Scriitor'; dacă (class_exists ($ class)) // returnează un nou obiect Writer returnează $ new class ();  // altfel nu reușim să aruncăm o nouă excepție ("Format neacceptat"); 

După cum am spus, există multe alte strategii de utilizat în funcție de cerințele dvs. În acest exemplu, o variabilă de solicitare alege formatul utilizat. Construiește un nume de clasă din variabila de cerere, verifică dacă există, apoi returnează un nou obiect Writer. Dacă nu există niciunul sub acel nume, o excepție este aruncată pentru a lăsa codul clientului să înțeleagă ce trebuie să facă.


Pasul 5: puneți-i pe toți împreună

Cu totul în loc, iată cum codul clientului nostru le-ar pune împreună:

$ article = new poly_base_Article ("Polimorfism", "Steve", timp (), 0); încercați $ writer = poly_base_Factory :: getWriter ();  captură (excepție $ e) $ writer = new poly_writer_XMLWriter ();  echo $ article-> write ($ writer);

Mai intai am creat un exemplu de articol cu ​​care sa lucram. Apoi, încercăm să obținem un obiect Writer din fabrică, returnându-ne la o valoare implicită (XMLWriter) dacă este aruncată o excepție. În cele din urmă, trecem obiectul Writer la articolele noastre scrie() metoda, imprimarea rezultatului.


Concluzie

În acest tutorial, v-am oferit o introducere în polimorfism și o explicație a interfețelor în PHP. Sper că îți dai seama că ți-am arătat doar un caz potențial de utilizare a polimorfismului. Există multe, multe alte aplicații. Polimorfismul este o modalitate elegantă de a scăpa de declarațiile condiționale urât în ​​codul dvs. OOP. Urmează principiul menținerii componentelor dvs. separate și face parte integrantă din multe modele de design. Dacă aveți întrebări, nu ezitați să întrebați în comentarii!

Cod