Objektorientert autolading i WordPress, del 2

I den forrige veiledningen dekket vi en håndfull konsepter, som alle vil være nødvendige for å forstå hva vi gjør i denne opplæringen.

Nærmere bestemt dekket vi følgende emner:

  • objektorientert grensesnitt
  • prinsippet om enkeltansvar
  • hvordan disse ser ut i PHP
  • hvor vi er på vei med pluginet vårt

I noen serier er det enkelt å hoppe over opplæringsprogrammer som kanskje ikke bygger på hverandre. Denne serien er imidlertid ikke ment å være slik. I stedet er det ment å bli lest i sekvensiell rekkefølge, og det er ment å bygge på innholdet i hver tidligere opplæring.

Med det sagt antar jeg at du er helt opptatt. 

Starter

Selv om jeg kanskje har nevnt dette i den første opplæringen, vil jeg likevel sørge for at vi er alle på samme side med hensyn til hva vi gjør i hver opplæring og med hvilken programvare du trenger.

Vår veikart

Så i denne opplæringen er planen som følger:

  1. Undersøk koden som vi har skrevet så langt.
  2. Bestem hvordan vi kan refactor det ved hjelp av objektorienterte teknikker.
  3. Gi høydepunktet for implementeringen.

Til slutt vil vi ikke skrive mye kode i denne opplæringen, men vi skal skrive noe. Det er imidlertid en praktisk opplæring ved at vi utfører objektorientert analyse og design. Dette er en nødvendig fase for mange store prosjekter (og noe som skal skje for småskala prosjekter).

Hva trenger du

Hvis du har fulgt med, bør du ha dette allerede satt opp. Men for å være sikker, her er den korte versjonen av alt du trenger:

  • et lokalt utviklingsmiljø som passer for operativsystemet ditt
  • en katalog ut av hvilken WordPress 4.6.1 er vert
  • en tekstredigerer eller IDE
  • kunnskap om WordPress Plugin API

Med alt dette på plass, er vi klare til å jobbe med koden som ble delt i den forrige opplæringen. Så la oss komme i gang.

Analysere koden

Det aller første vi vil gjøre er å analysere dagens autoloaderstatus. Det kan virke som mye kode for å lime inn i en enkelt blokk med kode, men det viser i seg selv at vi har noe arbeid å gjøre.

Med det sagt, her er nåværende status for vår autoloader:

 0; $ i--) // Les den nåværende delen av fildelen. $ current = strtolower ($ file_parts [$ i]); $ current = str_ireplace ('_', '-', $ current); // Hvis vi er ved første oppføring, så er vi på filnavnet. hvis (telle ($ file_parts) - 1 === $ i) / * Hvis "grensesnitt" er inkludert i delene av filnavnet, så * definer $ filnavn på en annen måte slik at den er riktig lastet inn. * Ellers må du bare sette $ filnavn samme som klassen * filnavn struktur. * / hvis (strpos (strtolower ($ file_parts [count ($ file_parts) - 1]), 'grensesnitt')) // Grab navnet på grensesnittet fra sitt kvalifiserte navn. $ interface_name = explode ('_', $ file_parts [count ($ file_parts) - 1]); $ interface_name = $ interface_name [0]; $ file_name = "interface- $ interface_name.php";  ellers $ file_name = "class- $ current.php";  ellers $ namespace = '/'. $ nåværende. $ Namespace;  // Nå bygger du en bane til filen ved hjelp av kartlegging til filplasseringen. $ filepath = trailingslashit (dirname (dirname (__FILE__)). $ namespace); $ filepath. = $ filnavn; // Hvis filen finnes i den angitte banen, så inkluder den. hvis (file_exists ($ filepath)) include_once ($ filepath);  else wp_die (esc_html ("Filen som forsøker å bli lastet inn på $ filepath eksisterer ikke.")); 

På dette punktet husk at prinsippet om enkeltansvar sier følgende:

En klasse skal bare ha en grunn til å forandre seg.

Akkurat nå har vi ikke engang en klasse, enn si flere individuelle metoder som bare har en eneste grunn til å forandre seg.

Og selv om det kan være fornuftig å begynne med å bryte denne autoloader-metoden i mindre, individuelle metoder, la oss starte fra et høyere nivå og begynne å tenke på en autoloader når det gjelder et grensesnitt. Da driller vi oss ned til å lage en klasse (eller klasser).

Objektorientert analyse: Ansvar

Husk fra den forrige veiledningen at et grensesnitt er definert av PHP manualen som følger:

Objektgrensesnitt lar deg lage kode som spesifiserer hvilke metoder en klasse må implementere, uten å måtte definere hvordan disse metodene håndteres.

Gitt koden og definisjonene ovenfor, la oss tenke på hva en autoloader trenger å gjøre fra et mer modulært perspektiv. Spesifikt, la oss bryte det ned i poeng som representerer det som kan være nok til å endre. Nei, vi kan ikke bruke alle disse punktene, men dette er hvorfor det kalles analyse. Vi jobber med design senere.

Koden gjør følgende:

  1. Bekreft at vi jobber eksplisitt med vårt navneområde.
  2. Splits innkommende klassenavn i deler for å avgjøre om det er en klasse eller et grensesnitt (slik $ class_name er et dårlig variabelt navn).
  3. Sjekker for å se om vi jobber med en grensesnittfil.
  4. Sjekker for å se om vi jobber med en klassefil.
  5. Sjekker for å se om vi jobber med et grensesnitt.
  6. Basert på utfallet av de ovennevnte betingelsene, genererer et filnavn.
  7. Bygger en filbane basert på det genererte filnavnet.
  8. Hvis filen eksisterer på det genererte navnet, inkluderer det.
  9. Ellers genererer koden en feil.

Således gjør koden ovenfor ni ting-det har den i det minste ni grunner til å endre - før det er ferdig med å fullføre sitt arbeid. 

Dette bør uten å si, men denne spesielle funksjonen er et perfekt eksempel på at vi kan refactor å demonstrere objektorientert analyse, design, grensesnitt og implementering.

Og dette reiser spørsmål: Hvor begynner vi selv?

Objektorientert analyse

På dette punktet er det rettferdig å si at vi kan begynne å gjøre objektorientert analyse, det vil si å se på hvilke potensielle klasser vi kan ha og hvordan de samhandler - gitt alt vi har oppført ovenfor. Husk at vi også vil at prinsippet om enkeltansvar skal hjelpe oss med å bestemme oss.

På dette tidspunktet er vi ikke veldig bekymret for hvordan klassene vil kommunisere med hverandre. I stedet er vi mer fokusert på å lage klasser som har en enkelt grunn til å endre.

Med det sagt, skal jeg gi et utvalg sett av klasser som jeg tror kan fungere. Før du går videre, se på hva vi har gjort og forsøk på å komme opp med din egen liste. Da kan vi sammenligne notater.

Et ord om ferdigheter

Vær oppmerksom på at du kan ha en bedre ide enn hva som er oppført nedenfor, eller du kan ta noe bort fra det vi har delt. Uansett er dette en læringsøvelse. Vi forsøker å forbedre vår kode, vår organisasjon, og til slutt bli bedre programmerere.

Våre potensielle klasser

Gitt det jeg har oppført ovenfor, har jeg kommet opp med følgende klasser:

  1. autolasteren. Dette er hovedklassen som er ansvarlig for å til slutt inkludere vår klasse, vårt navneområde eller grensesnittet vårt. Vi ringer denne klassen. Resten er klasser som skal ta vare på nødvendig arbeid som denne ene klassen må inkludere filen. 
  2. NamespaceValidator. Denne filen vil se på den innkommende klassen, grensesnittet, eller hva har du, og vil avgjøre om det er gyldig. Dette vil gi oss den avgjørende faktoren hvis vi kan fortsette med resten av koden vår ikke. 
  3. FileInvestigator. Denne klassen ser på typen fil som sendes inn i autoloaderen. Det vil avgjøre om det er en klasse, et grensesnitt eller et navneområde og returnere det fullt kvalifiserte banenavnet til filen slik at det kan være inkludert.
  4. FileRegistry. Dette vil bruke den fullt kvalifiserte filbanen til slutt returnert fra de andre klassene og vil inkludere den i plugin.

Og det er det. Nå er tredjepartsklasser i pluginet bare nødt til å vite om autoloader-klassen, men autoloader vil trenge kunnskaper om en annen klasse, og andre klasser vil trenge kunnskaper om ennå andre klasser.

Der er måter å håndtere dette (ved hjelp av avhengighetsinjektionsbeholdere, men det er utenfor rammen av dette prosjektet). Men det vi vil sikte på gjennom vår kode, er å minimere hvor mange klasser kjenner til hverandre.

Objektorientert design

På dette punktet vil forskjellige utviklere, firmaer, byråer og lag ta en annen tilnærming til hvordan de utformer systemet de jobber med.

En av de vanligste måtene å gjøre ved å gjøre dette, er å bruke noe som kalles et UML-diagram. Selv om det er nyttig, er det ikke noe som er verdt å gjøre innenfor omfanget av denne opplæringen, fordi det vil kreve en hel annen opplæring for å forklare alle delene.

Så med tanke på vår veiledning, og siden vi jobber med så lite kode, prøver vi å stubbe ut hvordan hver av de ovennevnte klassene kan fungere før vi implementerer dem. På den måten får vi en ide om hvordan vi kan organisere koden vår.

Merk at vi ikke vil navngitte noen av denne koden enda, og ingen av denne koden skal implementeres eller testes mot WordPress enda. Vi kommer inn i det i neste opplæring.

La oss begynne med autolasteren og jobber derfra.

autolasteren

Husk at denne klassen er ansvarlig for å inkludere den nødvendige filen. Dette er filen som vil bli registrert hos spl_autoload_register funksjon. 

namespace_validator = nytt NamespaceValidator (); $ this-> file_registry = ny filregistrering ();  offentlige funksjonsbelastning ($ filnavn) if ($ this-> namespace_validator-> er_valid ($ filnavn)) $ this-> file_registry-> last ($ filnavn);  

Merk at denne klassen avhenger av NamespaceValidator og FileRegistry klasse. Vi vil se hver av disse i mer detalj om et øyeblikk.

NamespaceValidator

Denne filen vil se på det innkommende filnavnet og bestemme om det er gyldig. Dette gjøres ved å se på navneområdet i filnavnet.

Hvis filen gjør Faktisk tilhører vårt navneområde, da kan vi anta at det er trygt å laste inn filen vår.

FileInvestigator

Denne klassen gjør ganske mye arbeid, men en del av det gjøres via svært enkle, veldig små hjelpemetoder. Under utførelsen ser det på hvilken type fil den er bestått. 

Den henter deretter det fullt kvalifiserte filnavnet for typen fil.

get_file_name ($ file_parts, $ current, $ i); hvis (telle ($ file_parts) - 1! == $ i) $ filepath = trailingslashit ($ filepath);  returnere $ filepath;  privat funksjon get_file_name ($ file_parts, $ current, $ i) $ filnavn = "; hvis (count ($ file_parts) - 1 === $ i) if ($ this-> is_interface ($ file_parts)) $ filnavn = $ this-> get_interface_name ($ file_parts); annet $ filnavn = $ dette-> get_class_name ($ current); annet $ filnavn = $ dette-> get_namespace_name ($ current); returner $ filnavn;  privat funksjon is_interface ($ file_parts) return strpos (strtolower ($ file_parts [count ($ file_parts) - 1]), 'grensesnitt'); privatfunksjon get_interface_name ($ file_parts) $ interface_name = explode ('_' $ file_parts [count ($ file_parts) - 1]); $ interface_name = $ interface_name [0]; return "interface- $ interface_name.php"; privat funksjon get_class_name ($ current) return "class- $ current.php" ; privat funksjon get_namespace_name ($ current) return '/'. $ current;

Hvis det er en fil som kan refactored litt mer, så er dette det. Tross alt forsøker det å avgjøre om vi jobber med en klasse, et grensesnitt eller en klasse. En enkel fabrikk kan være bedre egnet til dette.

Når det kommer tid til å implementere koden, vil vi kanskje refactor dette videre. Inntil da er dette et foreløpig design som kan fungere godt nok.

FileRegistry

Dette vil bruke den fullt kvalifiserte filbanen og inkludere filen; Ellers vil det bruke WordPress API for å vise en feilmelding.

klasse FileRegistry private $ investigator; offentlig funksjon __construct () $ this-> investigator = ny FileInvestigator ();  offentlig funksjonsbelastning ($ filepath) $ filepath = $ this-> investigator-> get_filetype ($ filepath); $ filepath = rtrim (plugin_dir_path (dirname (__FILE__)), '/'). $ Filepath; hvis (file_exists ($ filepath)) include_once ($ filepath);  ellers wp_die (esc_html ('Den angitte filen eksisterer ikke.'));  

Et annet alternativ til å bruke WordPress API ville være å kaste en egendefinert Unntaksmelding. På den måten kan vi helt skille eller dekode vår kode fra WordPress.

Igjen, denne koden er en overføring fra den første autoloaderen. Under implementeringen kan vi også endre dette.

Konklusjon

OK, så vi har sett på den eksisterende koden for vår autoloader, og så har vi stubbet ut noen potensiell kode som vi kan bruke basert på noen objektorientert analyse og design.

Er løsningen vi arbeider for mer vedlikeholdsbar enn hva vi har? Absolutt. Skal dette fungere i sammenheng med WordPress og vårt eksisterende plugin? Vi vet ikke før vi begynner å hekte dette opp i pluginet vårt.

Som tidligere nevnt er det fortsatt Noen områder der vi muligens kunne refactor denne koden. Hvis vi treffer slike problemer når du implementerer koden vår i den endelige versjonen av pluginet, tar vi en titt på å gjøre akkurat det.

Uansett, koden vi har nå, skal være lesbarere (selv om vi fortsatt har DocBlocks og noen inline kommentarer å introdusere) og mer vedlikeholdsbar og enda mer testbar.

Med det hele sagt, håper jeg at dette har gitt deg en ide om hvordan man skal ta en lang metode og bryte den inn i mer hensiktsmessige klasser. Jo, å ha flere klasser kan føle seg rart i begynnelsen, men det betyr ikke at det er en dårlig ting. Har flere filer (og dermed klasser) med mindre kode enn en fil med mye kode er bedre.

Embrace den motintuitive naturen av objektorientert programmering i denne forbindelse. I den neste opplæringen kommer vi til å returnere til pluginet vårt og vil jobbe med å implementere en variant av koden ovenfor. Vi vil sannsynligvis feilsøke noe av det også. Tross alt, sjelden får vi det riktig første gang

Inntil da, hvis du er interessert i å lese mer om objektorientert programmering i sammenheng med WordPress, kan du finne alle mine tidligere opplæringsprogrammer på min profilside. Følg meg på bloggen min eller følg meg på Twitter der jeg ofte snakker om begge.

ressurser

  • Objektorientert autolading i WordPress, del 1
  • navnerom
  • autoloading
  • grensesnitt
  • WordPress Plugin API
  • Enkelt ansvar prinsipp