Autoloading orientato agli oggetti in WordPress, parte 2

Nel precedente tutorial, abbiamo trattato una serie di concetti, che saranno necessari per comprendere appieno ciò che stiamo facendo in questo tutorial.

Nello specifico, abbiamo trattato i seguenti argomenti:

  • interfacce orientate agli oggetti
  • il principio della responsabilità unica
  • come appaiono in PHP
  • dove siamo diretti con il nostro plugin

In alcune serie, è facile saltare le esercitazioni che non si possono costruire l'una sull'altra; tuttavia, questa serie non è destinata ad essere così. Invece, è pensato per essere letto in ordine sequenziale, ed è pensato per costruire sul contenuto di ogni tutorial precedente.

Detto questo, presumo che tu sia coinvolto. 

Iniziare

Anche se potrei averlo menzionato nel primo tutorial, mi piace ancora assicurarmi che siamo tutti sulla stessa pagina per quanto riguarda ciò che stiamo facendo in ogni tutorial e con quale software avremo bisogno.

La nostra tabella di marcia

Quindi in questo tutorial, il piano è il seguente:

  1. Esaminare il codice che abbiamo scritto finora.
  2. Determina come possiamo essere in grado di refactoring utilizzando tecniche orientate agli oggetti.
  3. Fornire lo schema ad alto livello per la nostra implementazione.

In definitiva, non scriveremo tanto codice in questo tutorial, ma ne scriveremo alcuni. È, tuttavia, un tutorial pratico in quanto stiamo eseguendo analisi e progettazione orientata agli oggetti. Questa è una fase necessaria per molti progetti su larga scala (e qualcosa che dovrebbe accadere per progetti su piccola scala).

Quello di cui hai bisogno

Se hai seguito, dovresti averlo già impostato. Ma per essere sicuri, ecco la versione breve di tutto ciò di cui hai bisogno:

  • un ambiente di sviluppo locale adatto al tuo sistema operativo
  • una directory da cui è stato ospitato WordPress 4.6.1
  • un editor di testo o IDE
  • conoscenza dell'API del plugin WordPress

Con tutto ciò che è in atto, siamo pronti a lavorare sul codice condiviso nel tutorial precedente. Quindi iniziamo.

Analizzando il codice

La prima cosa che vogliamo fare è analizzare lo stato attuale del nostro autoloader. Potrebbe sembrare un sacco di codice da incollare in un singolo blocco di codice, ma questo di per sé ci mostra che abbiamo del lavoro da fare.

Detto questo, ecco lo stato attuale del nostro autoloader:

 0; $ i--) // Legge il componente corrente della parte del file. $ current = strtolower ($ file_parts [$ i]); $ corrente = str_ireplace ('_', '-', $ corrente); // Se siamo alla prima voce, allora siamo al nome del file. if (count ($ file_parts) - 1 === $ i) / * Se 'interface' è contenuta nelle parti del nome del file, quindi * definire il $ file_name in modo diverso in modo che sia caricato correttamente. * Altrimenti, basta impostare $ nome_file uguale a quello della struttura del nome * della classe. * / if (strpos (strtolower ($ file_parts [count ($ file_parts) - 1]), 'interface')) // Prendi il nome dell'interfaccia dal suo nome qualificato. $ interface_name = explode ('_', $ file_parts [count ($ file_parts) - 1]); $ interface_name = $ interface_name [0]; $ file_name = "interfaccia- $ interface_name.php";  else $ file_name = "class- $ current.php";  else $ namespace = '/'. $ corrente. $ Namespace;  // Ora crea un percorso per il file usando l'associazione al percorso del file. $ filepath = trailingslashit (dirname (dirname (__FILE__)). $ namespace); $ filepath. = $ nome_file; // Se il file esiste nel percorso specificato, quindi includerlo. if (file_exists ($ filepath)) include_once ($ filepath);  else wp_die (esc_html ("Il file che tenta di essere caricato in $ filepath non esiste.")); 

A questo punto, ricorda che il principio di responsabilità unica afferma quanto segue:

Una classe dovrebbe avere solo una ragione per cambiare.

Al momento, non abbiamo nemmeno una classe, per non parlare di più metodi individuali che hanno un solo motivo per cambiare.

E anche se potrebbe aver senso interrompere questo metodo di caricamento automatico in metodi individuali più piccoli, partiamo da un livello più alto e iniziamo a pensare a un caricatore automatico in termini di un'interfaccia. Quindi approfondiremo la creazione di una classe (o classi).

Analisi orientata agli oggetti: responsabilità

Ricorda dal tutorial precedente che un'interfaccia è definita dal manuale PHP come segue:

Le interfacce degli oggetti consentono di creare codice che specifica i metodi che una classe deve implementare, senza dover definire come vengono gestiti questi metodi.

Dato il codice e le definizioni di cui sopra, pensiamo a ciò che un autoloader deve fare da una prospettiva più modulare. Nello specifico, suddividiamolo in punti che rappresentano ciò che potrebbe essere sufficiente per cambiare. No, non possiamo usare tutti questi punti, ma è per questo che si chiama analisi. Lavoreremo sul design più tardi.

Il codice esegue quanto segue:

  1. Convalida che stiamo lavorando in modo esplicito con il nostro spazio dei nomi.
  2. Divide il nome della classe in entrata in parti per determinare se si tratta di una classe o di un'interfaccia (così $ class_name è un nome di variabile povero).
  3. Controlla se stiamo lavorando con un file di interfaccia.
  4. Controlla se stiamo lavorando con un file di classe.
  5. Controlla se stiamo lavorando con un'interfaccia.
  6. Basato sul risultato dei condizionali sopra, genera un nome di file.
  7. Crea un percorso file basato sul nome file generato.
  8. Se il file esiste con il nome generato, lo include.
  9. In caso contrario, il codice genera un errore.

Quindi, il codice sopra fa nove cose-cioè, ha almeno nove motivi per cambiare, prima che sia terminato il suo lavoro. 

Questo dovrebbe essere ovvio, ma questa particolare funzione è un esempio perfetto che possiamo refactoring per dimostrare l'analisi orientata agli oggetti, la progettazione, le interfacce e l'implementazione.

E questo solleva una domanda: dove iniziamo??

Analisi orientata agli oggetti

A questo punto, è giusto dire che possiamo iniziare a fare analisi orientate agli oggetti, cioè guardando a quali classi potenziali possiamo avere e come interagiscono, dato tutto ciò che abbiamo elencato sopra. Ricorda, vogliamo anche che il principio della responsabilità unica ci aiuti a guidarci nel nostro processo decisionale.

A questo punto, non siamo terribilmente preoccupati di come le classi comunicheranno tra loro. Invece, siamo più concentrati sulla creazione di classi che hanno una sola ragione per cambiare.

Detto ciò, fornirò una serie di classi che penso possano funzionare. Prima di andare oltre, guarda cosa abbiamo fatto e prova a creare la tua lista personale. Quindi possiamo confrontare le note.

Una parola sulle competenze

Tieni presente che potresti avere un'idea migliore rispetto a quanto elencato di seguito, oppure puoi prendere qualcosa di diverso da ciò che abbiamo condiviso. Indipendentemente da ciò, questo è un esercizio di apprendimento. Stiamo tentando di migliorare il nostro codice, la nostra organizzazione e, in definitiva, diventare dei programmatori migliori.

Le nostre lezioni potenziali

Dato quello che ho elencato sopra, ho creato le seguenti classi:

  1. Autoloader. Questa è la classe principale che è responsabile, in ultima analisi, della nostra classe, del nostro spazio dei nomi o della nostra interfaccia. Chiameremo questa classe. Il resto sono classi che si prenderanno cura del lavoro necessario che questa classe deve includere nel file. 
  2. NamespaceValidator. Questo file esaminerà la classe, l'interfaccia o quello che hai in arrivo e determinerà se è valido. Questo ci darà il fattore decisivo se possiamo procedere con il resto del nostro codice, nostro no. 
  3. FileInvestigator. Questa classe esamina il tipo di file che viene passato nel caricatore automatico. Determina se si tratta di una classe, un'interfaccia o uno spazio dei nomi e restituisce il nome del percorso completo al file in modo che possa essere incluso.
  4. FileRegistry. In questo modo verrà utilizzato il percorso file completo che verrà restituito dalle altre classi e che verrà incluso nel plug-in.

E questo è tutto. Ora le classi di terze parti nel nostro plugin devono solo conoscere la classe del caricatore automatico, ma il caricatore automatico avrà bisogno della conoscenza di un'altra classe, e altre classi avranno bisogno della conoscenza di altre classi ancora.

Là siamo modi per gestirlo (usando i contenitori per le dipendenze, ma questo va oltre lo scopo di questo progetto). Ma quello che cercheremo di fare attraverso il nostro codice è minimizzare il numero di classi che conoscono l'un l'altro.

Design orientato agli oggetti

A questo punto, diversi sviluppatori, aziende, agenzie e team avranno un approccio diverso a come progettano il sistema su cui stanno lavorando.

Uno dei modi più comuni per fare ciò è usare qualcosa chiamato un diagramma UML. Anche se è utile, non è qualcosa che vale la pena fare nell'ambito di questo tutorial perché richiederà un intero tutorial per spiegare tutti i pezzi.

Quindi, ai fini del nostro tutorial, e dal momento che stiamo lavorando con una quantità così piccola di codice, cercheremo di eliminare il modo in cui ognuna delle classi precedenti potrebbe funzionare prima di implementarle. In questo modo, avremo un'idea di come possiamo organizzare il nostro codice.

Nota che non ci sarà ancora il namespace di questo codice, e nessuno di questo codice dovrebbe essere implementato o testato ancora su WordPress. Ne parleremo nel prossimo tutorial.

Iniziamo con Autoloader e lavora da lì.

Autoloader

Ricorda, questa classe è responsabile per includere il file necessario. Questo è il file che verrà registrato con spl_autoload_register funzione. 

namespace_validator = new NamespaceValidator (); $ this-> file_registry = new FileRegistry ();  load di funzione pubblica ($ filename) if ($ this-> namespace_validator-> is_valid ($ filename)) $ this-> file_registry-> load ($ filename);  

Si noti che questa classe dipende da NamespaceValidator e il FileRegistry classe. Vedremo ognuno di questi in modo più dettagliato in un solo momento.

NamespaceValidator

Questo file controllerà il nome file in ingresso e determinerà se è valido. Questo viene fatto guardando lo spazio dei nomi nel nome del file.

Se il file fa in effetti appartengono al nostro spazio dei nomi, quindi possiamo supporre che sia sicuro caricare il nostro file.

FileInvestigator

Questa classe sta facendo un bel po 'di lavoro, anche se parte di essa viene eseguita tramite metodi di supporto molto semplici e molto piccoli. Durante il corso dell'esecuzione, esamina il tipo di file che è passato. 

Quindi recupera il nome file completo per il tipo di file.

get_file_name ($ file_parts, $ current, $ i); if (count ($ file_parts) - 1! == $ i) $ filepath = trailingslashit ($ filepath);  restituisce $ filepath;  funzione privata 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 function is_interface ($ file_parts) return strpos (strtolower ($ file_parts [count ($ file_parts) - 1]), 'interface'); private function get_interface_name ($ file_parts) $ interface_name = explode ('_', $ file_parts [count ($ file_parts) - 1]); $ interface_name = $ interface_name [0]; return "interface- $ interface_name.php"; private function get_class_name ($ current) return "class- $ current.php" ; private function get_namespace_name ($ current) return '/'. $ current;

Se c'è un file che può essere refactored un po 'di più, allora è questo. Dopo tutto, tenta di determinare se stiamo lavorando con una classe, un'interfaccia o una classe. Una fabbrica semplice potrebbe essere più adatta a questo.

Quando arriva il momento di implementare il nostro codice, forse lo rifatteremo ulteriormente. Fino ad allora, questo è un progetto preliminare che potrebbe funzionare abbastanza bene.

FileRegistry

Questo utilizzerà il percorso file completo e includerà il file; in caso contrario, utilizzerà l'API di WordPress per visualizzare un messaggio di errore.

class FileRegistry private $ investigator; funzione pubblica __construct () $ this-> investigator = new FileInvestigator ();  load di funzione pubblica ($ filepath) $ filepath = $ this-> investigator-> get_filetype ($ filepath); $ filepath = rtrim (plugin_dir_path (dirname (__FILE__)), '/'). $ Percorsofile; if (file_exists ($ filepath)) include_once ($ filepath);  else wp_die (esc_html ('Il file specificato non esiste.'));  

Un'altra alternativa all'utilizzo dell'API di WordPress è rappresentata dal lancio di un messaggio di eccezione personalizzato. In questo modo, saremmo in grado di separare o disaccoppiare completamente il nostro codice da WordPress.

Ancora una volta, questo codice è un riporto dal caricatore automatico iniziale. Durante l'implementazione, possiamo cambiare anche questo.

Conclusione

Bene, quindi abbiamo esaminato il codice esistente per il nostro autoloader e quindi abbiamo eliminato un potenziale codice che è possibile utilizzare in base ad analisi e progettazione orientate agli oggetti.

La soluzione su cui stiamo lavorando è più gestibile di quella che abbiamo? Assolutamente. Funzionerà nel contesto di WordPress e del nostro plug-in esistente? Non lo sapremo fino a quando non inizieremo a collegarlo al nostro plug-in.

Come accennato in precedenza, ci sono ancora alcune aree in cui potremmo eventualmente refactoring questo codice. Se riscontriamo questo tipo di problemi quando implementiamo il nostro codice nella versione finale del nostro plugin, daremo un'occhiata a questo.

In ogni caso, il codice che abbiamo ora dovrebbe essere più leggibile (anche se abbiamo ancora DocBlocks e alcuni commenti in linea da introdurre) e più manutenibile e ancor più verificabile.

Con tutto ciò detto, spero che questo ti abbia dato un'idea su come prendere un metodo lungo e suddividerlo in classi più orientate allo scopo. Certo, avere classi multiple potrebbe sembrare strano all'inizio, ma questo non significa che sia una brutta cosa. Avere più file (e quindi classi) con meno codice di un file con un sacco di codice è meglio.

Abbracciare la natura controintuitiva della programmazione orientata agli oggetti in questo senso. Nel prossimo tutorial, ritorneremo al nostro plug-in e lavoreremo per implementare una variante del codice sopra. Probabilmente eseguiremo il debug di alcuni di essi. Dopotutto, raramente lo facciamo bene la prima volta

Fino ad allora, se sei interessato a leggere di più sulla programmazione orientata agli oggetti nel contesto di WordPress, puoi trovare tutti i miei tutorial precedenti sulla mia pagina del profilo. Sentiti libero di seguire il mio sul mio blog o seguimi su Twitter, dove spesso parlo di entrambi.

risorse

  • Autoloading orientato agli oggetti in WordPress, parte 1
  • Namespace
  • Caricamento automatico
  • interfacce
  • L'API del plugin WordPress
  • Principio della singola responsabilità