2011-10-06 18 views
6

Faccio spesso progetti PHP progettati per raschiare dati gerarchici da pagine Web e salvarli nel DB (essenzialmente, strutturare i dati - pensare di raschiare i siti web governativi che hanno i dati, ma non li forniscono in modo strutturato). Ogni volta, cerco di trovare un design OOP che mi avrebbe permesso di raggiungere i seguenti:Progettazione OOP PHP - limitazione dei parametri a classi figlio specifiche durante l'implementazione di interfacce generiche

  • sostituire facilmente gli script di parsing HTML attuali con quelli nuovi, nel caso in cui la pagina web originale cambia
  • consentono una facile estensioni i dati vengono raschiati e salvati, poiché questi progetti sono anche pensati per gli altri da prendere e sviluppare. Il mio obiettivo è raccogliere i dati "di base", mentre altri potrebbero decidere di includere qualcosa in più, cambiare il modo in cui è salvato ed ecc.

Finora sono ancora riuscito a trovare la soluzione, ma il più vicino che ho ottenuto è qualcosa di simile:

mi definiscono una classe astratta per i contenitori di dati che avrebbe implementare funzioni comuni ad albero di movimento:

abstract class DataContainer { 

    protected $parent = NULL; 
    protected $children = NULL; 

    public function getParent() { 
    return $this->parent; 
    } 

    public function getChildren() { 
    return $this->children; 
    }    
} 

E poi ho i contenitori di dati effettivi. Immagina, sto raschiando i dati sulla partecipazione alle sessioni parlamentari a un livello di "domanda specifica in una seduta". Avrei SessionContainer, SittingContainer, QuestionContainer che estenderebbe tutto il DataContainer.

Ciascuna sessione, seduta e dati delle domande vengono raschiati da un URL diverso. Lasciando da parte il meccanismo di mettere da parte il contenuto dell'URL, diciamo solo che ho bisogno delle classi di raschiatura, che prenderebbero i contenitori e un DOmDocument per l'analisi vera e propria. Quindi mi sento di definire un'interfaccia generica come questa:

interface Scraper { 
    public function scrapeData(DOMDocument $Dom, DataContainer $DataContainer); 
} 

Poi, ciascuna della sessione, seduta e domanda sarebbe hanno le loro ruspe, che implementano l'interfaccia. Ma vorrei anche assicurarmi che possano accettare solo i contenitori a cui sono destinati. Quindi sarebbe simile:

class SessionScraper implements Scraper { 
    public function scrapeData(DOMDocument $DOM, SessionContainer $DataContainer) { 
    } 
} 

Infine, avrei un generico Factory classe che implementa anche l'interfaccia raschietto e proprio distribuisce la raschiatura di raschietti rilevanti. In questo modo:

public function scrapeData(DOMDocument $DOM, DataContainer $DataContainer) { 
    //get the scraper from configuration array 
    $class = $this->config[get_class($DataContainer)]; 
    $craper = new $class(); 
    $class->scrapeData($DOM, $DataContainer); 
} 

Questa è la classe che verrebbe effettivamente chiamata nel codice. In modo molto simile, potrei occuparmi di salvare in DB: ogni contenitore di dati potrebbe avere la sua classe DBSaver, che implementerebbe l'interfaccia DBSaver. Ancora una volta, tutte le chiamate potrebbero essere effettuate tramite la classe Factory, che implementerebbe anche l'interfaccia DBSaver.

Tutto sarebbe perfetto, ma il problema è che le classi che implementano l'interfaccia dovrebbero implementare la firma esatta dell'interfaccia. Per esempio. il metodo SessionScraper::scrapeData non può accettare soloSessionContainer oggetti, deve accettare tutti gli oggetti DataContainer. Ma non è destinato a!

Infine, la questione:

  • È il mio disegno sbagliato e dovrei essere Strutturare tutto in un modo completamente diverso? (Come?), oppure:
  • Il mio progetto è OK, è solo che ho bisogno di applicare i tipi all'interno dei metodi con instanceof e controlli simili invece di imporlo tramite typehinting?

Grazie in anticipo per tutti i suggerimenti/critiche. Sono completamente felice con qualcuno che capovolge questo codice sulla sua testa, se necessario!

risposta

2

Container molle negli occhi. Questo nome è molto generico, potresti aver bisogno di qualcosa di più dinamico. Penso che tu abbia Data e tu classify esso, quindi ha un type.

Quindi, invece, è necessario codificare l'interfaccia esatta nell'hint di tipo, è necessario risolverlo dinamicamente.

Se ora ogni Container avrebbe un type, il Scraper potrebbe segnalare/dire se sia o non è applicabile per il type di Container.

La forma concreta di raschiatura è in realtà la strategia di si utilizza per i dati specifici di analizzarlo. Il tuo contenitore incapsula la strategia fornendo un'interfaccia per i dati normalizzati.

Hai solo solo bisogno di aggiungere un po 'di logica/contratto tra Container e Scraper in modo che possano parlare tra di loro. Questo contratto puoi inserire nell'interfaccia di entrambi.

Ciò consentirebbe anche di avere un Scraper che può gestire più types se si desidera allungarlo.

Per il vostro Container, date un'occhiata a SPL e implementate alcune interfacce in modo che siano disponibili iteratori (e iteratori ricorsivi). Questa potrebbe essere la struttura generica a cui ti stai riferendo e SPL potrebbe aumentare l'usabilità delle tue classi Container.

Non è necessario codificare tutto in OOP, è possibile mantenere le cose dinamiche e, soprattutto in PHP, si risolvono normalmente le cose in fase di esecuzione.

Ciò consentirà inoltre di sostituire più facilmente Scrapers con una nuova versione. Come Scrapers ora avremmo un tipo per definizione (come suggerito sopra), è possibile risolvere in fase di esecuzione quale classe concreta dovrebbe eseguire lo scraping, ad es. caricandoli dinamicamente da un file .php in una bella struttura di file system.

Solo i miei 2 centesimi.

+0

grazie per la risposta esauriente - innescato un paio di altre idee, anche! Un chiarimento: comprendo correttamente che essenzialmente si suggerisce di avere una classe Data/Container per conservare tutti i dati e identificarla per tipo di proprietà anziché creare classi figlio? Oppure sarebbe sia di tipo proprietà che di classi figlio, solo i raschietti terrebbero conto solo del tipo? – Aurimas

+0

Non conosco i dati in modo specifico, quindi è difficile dirlo. Se i dati sono molto comuni ha solo proprietà differenti, non è necessario creare molte classi di dati, puoi andare con le proprietà dinamiche. Questo è molto meglio per l'applicazione generale più tardi. Per lo più i raschiatori cambieranno, a volte i dati con esso. Dovresti sempre creare una nuova classe di dati solo perché alcuni siti web sono leggermente cambiati. Non bene :) – hakre

Problemi correlati