Objectgericht Autoloading in WordPress, deel 2

In de vorige tutorial hebben we een handvol concepten besproken, die allemaal nodig zullen zijn om volledig te begrijpen wat we in deze tutorial doen.

Concreet hebben we de volgende onderwerpen behandeld:

  • objectgeoriënteerde interfaces
  • het beginsel van één enkele verantwoordelijkheid
  • hoe deze eruit zien in PHP
  • waar we naartoe gaan met onze plug-in

In sommige series is het eenvoudig om zelfstudies over te slaan die niet op elkaar kunnen voortbouwen; deze reeks is echter niet bedoeld om zo te zijn. In plaats daarvan is het bedoeld om in een sequentiële volgorde te worden gelezen en het is de bedoeling dat wordt voortgebouwd op de inhoud van elke vorige zelfstudie.

Met dat gezegd, neem ik aan dat je helemaal bent ingehaald. 

Ermee beginnen

Hoewel ik dit misschien in de eerste tutorial heb genoemd, wil ik er nog steeds zeker van zijn dat we allemaal op dezelfde pagina staan ​​met betrekking tot wat we in elke tutorial doen en met welke software je die nodig zult hebben.

Onze routekaart

Dus in deze tutorial is het plan als volgt:

  1. Bestudeer de code die we tot nu toe hebben geschreven.
  2. Bepaal hoe we het kunnen refactiveren met behulp van object-georiënteerde technieken.
  3. Geef een overzicht op hoog niveau voor onze implementatie.

Uiteindelijk zullen we niet schrijven veel code in deze tutorial, maar we zullen er wat schrijven. Het is echter een praktische tutorial omdat we objectgeoriënteerde analyses en ontwerpen uitvoeren. Dit is een noodzakelijke fase voor veel grootschalige projecten (en iets dat zou moeten gebeuren voor kleinschalige projecten).

Wat je nodig hebt

Als je meegekeken hebt, moet je dit al hebben ingesteld. Maar om zeker te zijn, hier is de korte versie van alles wat je nodig hebt:

  • een lokale ontwikkelomgeving die geschikt is voor uw besturingssysteem
  • een map waaruit WordPress 4.6.1 wordt gehost
  • een teksteditor of IDE
  • kennis van de WordPress Plugin API

Met dat alles op zijn plaats, zijn we klaar om te werken aan de code die in de vorige tutorial werd gedeeld. Dus laten we beginnen.

Analyse van de code

Het allereerste dat we willen doen is de huidige staat van onze autoloader analyseren. Het lijkt misschien veel code om in een enkel codeblok te plakken, maar dat laat op zich al zien dat we wat werk te doen hebben.

Met dat gezegd, hier is de huidige staat van onze autoloader:

 0; $ i--) // Lees de huidige component van het bestanddeel. $ current = strtolower ($ file_parts [$ i]); $ current = str_ireplace ('_', '-', $ current); // Als we bij de eerste invoer zijn, staan ​​we bij de bestandsnaam. if (count ($ file_parts) - 1 === $ i) / * Als 'interface' deel uitmaakt van de bestandsnaam, definieer * dan de $ file_name anders, zodat deze correct is geladen. * Anders zet je gewoon de $ file_name gelijk aan die van de class * bestandsnaamstructuur. * / if (strpos (strtolower ($ file_parts [count ($ file_parts) - 1]), 'interface')) // Pak de naam van de interface uit zijn gekwalificeerde naam. $ interface_name = ontploffen ('_', $ file_parts [count ($ file_parts) - 1]); $ interface_name = $ interface_name [0]; $ file_name = "interface- $ interface_name.php";  else $ file_name = "class- $ current.php";  else $ namespace = '/'. $ stroom. $ Namespace;  // Bouw nu een pad naar het bestand met mapping naar de bestandslocatie. $ filepath = trailingslashit (dirname (dirname (__FILE__)). $ namespace); $ filepath. = $ bestandsnaam; // Als het bestand bestaat in het opgegeven pad, neemt u het op. if (file_exists ($ filepath)) include_once ($ filepath);  else wp_die (esc_html ("Het bestand dat probeert te worden geladen op $ filepath bestaat niet.")); 

Houd er op dit moment rekening mee dat het beginsel van een enkele verantwoordelijkheid het volgende bepaalt:

Een klasse zou maar één reden moeten hebben om te veranderen.

Op dit moment hebben we niet eens een klas, laat staan ​​meerdere individuele methoden die maar één reden hebben om te veranderen.

En hoewel het misschien logisch is om te beginnen met het doorbreken van deze autoloadermethode in kleinere, afzonderlijke methoden, laten we beginnen met een hoger niveau en beginnen na te denken over een autoloader in termen van een interface. Daarna gaan we dieper in op het maken van een les (of klassen).

Object-georiënteerde analyse: verantwoordelijkheden

Roep uit de vorige tutorial op dat een interface als volgt wordt gedefinieerd door de PHP-handleiding:

Met objectinterfaces kunt u een code maken die aangeeft welke methoden een klasse moet implementeren, zonder dat u hoeft te definiëren hoe deze methoden worden behandeld.

Gegeven de code en de bovenstaande definities, laten we eens nadenken over wat een autoloader moet doen vanuit een meer modulair perspectief. Laten we het in het bijzonder opsplitsen in punten die weergeven wat misschien genoeg is om te veranderen. Nee, we mogen niet al deze punten gebruiken, maar dit is waarom het de analyse wordt genoemd. We zullen later aan het ontwerp werken.

De code doet het volgende:

  1. Valideert dat we expliciet werken met onze naamruimte.
  2. Splitst de inkomende klassennaam in delen om te bepalen of het een klasse of een interface is (dus $ class_name is een slechte variabele naam).
  3. Controleert of we met een interfacebestand werken.
  4. Controleert of we met een klassenbestand werken.
  5. Controleert of we met een interface werken.
  6. Op basis van de uitkomst van de bovenstaande conditionals, genereert een bestandsnaam.
  7. Bouwt een bestandspad op gebaseerd op de gegenereerde bestandsnaam.
  8. Als het bestand bestaat bij de gegenereerde naam, neemt u het op.
  9. Anders genereert de code een fout.

Dus, de bovenstaande code doet negen dingen-dat wil zeggen, het heeft tenminste negen redenen om te veranderen voordat het klaar is met het voltooien van zijn werk. 

Dit zou vanzelfsprekend moeten zijn, maar deze specifieke functie is een perfect voorbeeld dat we kunnen refactoren om objectgeoriënteerde analyse, ontwerp, interfaces en implementatie aan te tonen..

En dit werpt een vraag op: waar beginnen we eigenlijk??

Object-georiënteerde analyse

Op dit punt is het redelijk om te zeggen dat we objectgeoriënteerde analyses kunnen gaan doen, dat wil zeggen, kijken naar welke potentiële klassen we kunnen hebben en hoe ze op elkaar inwerken, gegeven alles wat we hierboven hebben genoemd. Vergeet niet dat we ook willen dat het beginsel van één enkele verantwoordelijkheid ons helpt bij het nemen van beslissingen.

Op dit punt zijn we niet erg bezorgd over hoe de klassen met elkaar zullen communiceren. In plaats daarvan zijn we meer gericht op het maken van klassen die maar één reden hebben om te veranderen.

Met dat gezegd, ga ik een aantal voorbeelden van lessen geven waarvan ik denk dat ze zouden kunnen werken. Kijk voordat je verder gaat naar wat we hebben gedaan en probeer een eigen lijst te maken. Dan kunnen we aantekeningen vergelijken.

Een woord over vaardigheden

Houd er rekening mee dat u misschien een beter idee hebt dan wat hieronder wordt vermeld, of dat u iets weghaalt van wat we hebben gedeeld. Hoe dan ook, dit is een leeroefening. We proberen onze code, onze organisatie te verbeteren en uiteindelijk betere programmeurs te worden.

Onze potentiële klassen

Gezien wat ik hierboven heb vermeld, heb ik de volgende klassen bedacht:

  1. autoloader. Dit is de hoofdklasse die verantwoordelijk is voor het uiteindelijk opnemen van onze klasse, onze naamruimte of onze interface. We zullen deze klas noemen. De rest zijn klassen die voor het nodige werk zorgen dat deze klasse het bestand moet opnemen. 
  2. NamespaceValidator. Dit bestand zal kijken naar de inkomende klasse, interface of wat heeft u, en zal bepalen of het geldig is. Dit zal ons de beslissende factor geven als we door kunnen gaan met de rest van onze code, niet met ons. 
  3. FileInvestigator. Deze klasse kijkt naar het type bestand dat wordt doorgegeven aan de autoloader. Het zal bepalen of het een klasse, een interface of een naamruimte is en retourneert de volledig gekwalificeerde padnaam naar het bestand zodat het kan worden opgenomen.
  4. FileRegistry. Hiermee wordt het volledig gekwalificeerde bestandspad gebruikt dat uiteindelijk is geretourneerd uit de andere klassen en wordt het opgenomen in de plug-in.

En dat is het. Nu moeten klassen van derden in onze plug-in alleen over de autoloader-klasse weten, maar de autoloader moet kennis van een andere klasse hebben en andere klassen moeten kennis hebben van nog andere klassen.

Er zijn manieren om hiermee om te gaan (met afhankelijkheid van injectie-containers, maar dat valt buiten het bestek van dit project). Maar wat we met onze code willen bereiken, is het minimaliseren van hoeveel klassen van elkaar weten.

Objectgericht ontwerpen

Op dit punt zullen verschillende ontwikkelaars, bedrijven, bureaus en teams een andere benadering kiezen voor het ontwerp van het systeem waarop ze werken.

Een van de meest gebruikelijke manieren om dit te doen, is door iets te gebruiken dat een UML-diagram wordt genoemd. Hoewel het nuttig is, is het niet de moeite waard om te doen in het kader van deze tutorial omdat het een hele andere tutorial vereist om alle stukjes uit te leggen.

Dus voor de doeleinden van onze zelfstudie en omdat we met zo weinig code werken, proberen we uit te zoeken hoe elk van de bovenstaande klassen werkt voordat we ze implementeren. Op deze manier krijgen we een idee van hoe we onze code kunnen organiseren.

Houd er rekening mee dat we deze code nog niet namen, en geen enkele van deze code moet nog worden geïmplementeerd of getest tegen WordPress. Daar komen we in de volgende tutorial aan te pas.

Laten we beginnen met de autoloader en werk vanaf daar.

autoloader

Onthoud dat deze klasse verantwoordelijk is voor het opnemen van het benodigde bestand. Dit is het bestand dat zal worden geregistreerd bij de spl_autoload_register functie. 

namespace_validator = new NamespaceValidator (); $ this-> file_registry = new FileRegistry ();  openbare functie laden ($ bestandsnaam) if ($ this-> namespace_validator-> is_valid ($ filename)) $ this-> file_registry-> load ($ filename);  

Merk op dat deze klasse afhankelijk is van de NamespaceValidator en de FileRegistry klasse. We zullen elk van deze in een ogenblik meer in detail bekijken.

NamespaceValidator

Dit bestand kijkt naar de inkomende bestandsnaam en zal bepalen of het geldig is. Dit wordt gedaan door naar de naamruimte in de bestandsnaam te kijken.

Als het bestand doet in feite tot onze naamruimte behoren, dan kunnen we ervan uitgaan dat het veilig is om ons bestand te laden.

FileInvestigator

Deze klasse doet behoorlijk wat werk, hoewel een deel ervan wordt gedaan via zeer eenvoudige, zeer kleine hulpmethoden. Tijdens de uitvoering wordt gekeken naar het type bestand dat is doorgegeven. 

Vervolgens wordt de volledig gekwalificeerde bestandsnaam opgehaald voor het bestandstype.

get_file_name ($ file_parts, $ current, $ i); if (count ($ file_parts) - 1! == $ i) $ filepath = trailingslashit ($ filepath);  retourneer $ filepath;  persoonlijke functie get_file_name ($ file_parts, $ current, $ i) $ filename = "; if (count ($ file_parts) - 1 === $ i) if ($ this-> is_interface ($ file_parts)) $ filename = $ this-> get_interface_name ($ file_parts); else $ filename = $ this-> get_class_name ($ current); else $ filename = $ this-> get_namespace_name ($ current); return $ filename;  private functie is_interface ($ file_parts) return strpos (strtolower ($ file_parts [count ($ file_parts) - 1]), 'interface'); private functie get_interface_name ($ file_parts) $ interface_name = explode ('_', $ file_parts [count ($ file_parts) - 1]); $ interface_name = $ interface_name [0]; return "interface- $ interface_name.php"; persoonlijke functie get_class_name ($ current) return "class- $ current.php" ; persoonlijke functie get_namespace_name ($ current) return '/'. $ current;

Als er een bestand is dat iets meer kan worden herschreven, dan is dit het. Het probeert tenslotte om te bepalen of we met een klasse, een interface of een klas werken. Een eenvoudige fabriek kan hier beter geschikt voor zijn.

Wanneer het tijd is om onze code te implementeren, kunnen we dit misschien nog een keer refacteren. Tot die tijd is dit een voorlopig ontwerp dat goed genoeg kan werken.

FileRegistry

Hiermee wordt het volledig gekwalificeerde bestandspad gebruikt en wordt het bestand opgenomen; anders gebruikt het de WordPress API om een ​​foutmelding weer te geven.

class FileRegistry private $ investigator; public function __construct () $ this-> investigator = new FileInvestigator ();  public function load ($ filepath) $ filepath = $ this-> investigator-> get_filetype ($ filepath); $ filepath = rtrim (plugin_dir_path (dirname (__FILE__)), '/'). $ Filepath; if (file_exists ($ filepath)) include_once ($ filepath);  else wp_die (esc_html ('Het opgegeven bestand bestaat niet.'));  

Een ander alternatief voor het gebruik van de WordPress API zou zijn om een ​​aangepast uitzonderingsbericht te gooien. Op die manier zouden we onze code volledig kunnen scheiden of ontkoppelen van WordPress.

Nogmaals, deze code is een versleping van de initiële autoloader. Tijdens de implementatie kunnen we dit ook wijzigen.

Conclusie

Oké, dus we hebben gekeken naar de bestaande code voor onze autoloader, en dan hebben we een aantal mogelijke codes weggenomen die we kunnen gebruiken op basis van object-georiënteerde analyse en ontwerp.

Is de oplossing waar we naar streven meer onderhoudbaar dan wat we hebben? Absoluut. Gaat dit werken binnen de context van WordPress en onze bestaande plug-in? We zullen het niet weten voordat we dit in onze plug-in gaan opnemen.

Zoals eerder vermeld, zijn er nog steeds sommige gebieden waarin we deze code mogelijk zouden kunnen refacteren. Als we dit soort problemen tegenkomen bij het implementeren van onze code in de definitieve versie van onze plug-in, zullen we een kijkje nemen om precies dat te doen.

Hoe het ook zij, de code die we nu hebben, zou beter leesbaar moeten zijn (hoewel we nog steeds DocBlocks en enkele inline-opmerkingen hebben om te introduceren) en beter onderhouden en zelfs nog toetsbaarder.

Met dat alles gezegd, ik hoop dat dit je een idee heeft gegeven over hoe je een lange methode moet nemen en het in meer doelgerichte klassen kunt breken. Natuurlijk, het hebben van meerdere klassen kan in het begin raar aanvoelen, maar dat betekent niet dat het een slechte zaak is. Heb meer bestanden (en dus klassen) met minder code dan één bestand met veel code is beter.

Omarm de contra-intuïtieve aard van object-georiënteerd programmeren in dit opzicht. In de volgende zelfstudie gaan we terug naar onze plug-in en werken we aan de implementatie van een variant van de bovenstaande code. We zullen er waarschijnlijk ook iets van debuggen. We krijgen het immers zelden voor de eerste keer goed

Tot die tijd kun je, als je meer wilt lezen over objectgeoriënteerd programmeren in de context van WordPress, al mijn vorige tutorials op mijn profielpagina vinden. Voel je vrij om mijn te volgen op mijn blog of volg mij op Twitter, waar ik vaak over beide praat.

Middelen

  • Objectgericht Autoloading in WordPress, deel 1
  • namespaces
  • autoloading
  • interfaces
  • De WordPress Plugin-API
  • Single Responsibility Principle