Come posso forzare un oggetto che rispetta un'interfaccia per generare un determinato tipo / struttura di dati con le sue funzioni?

0

Questo è esattamente il mio codice, sto solo eliminando tutto ciò che non è necessario:

Prima di tutto, ho incontrato questo problema un sacco di volte e qualcosa mi dice che è qualcosa che ha a che fare con PHP stesso e "suggerimento sulla struttura dei dati"?.

Ho 3 oggetti, tutti rispettano la seguente interfaccia:

interface OutputExportData { function output(); }

Ciò che dice a qualsiasi oggetto che lo implementa è che l'oggetto ha bisogno di generare un qualche tipo di dati. Supponiamo che da qualche parte lungo la catena di esecuzione, c'è un foreach che passa attraverso questi 3 oggetti e dato che tutti hanno il 100% della funzione output , viene chiamato così posso ottenere i dati, il mio reale utilizzo effettivo è quello Eseguo un'importazione di diversi passaggi che salvano / wrangle un sacco di dati e questa funzione output mi fornirà un elenco di ID / riepilogo di ciò che è stato importato / modificato in base all'implementazione di quell'oggetto.

E qui c'è il problema. Ogni oggetto è libero di output tutto ciò che desiderano, o meglio comunque lo desiderano. Perché questo è un problema?

foreach( $objects as $object ) {
    $export_data = $object->output();
    saveToImportantList( $export_data['main_flags'] );
}

Per ogni oggetto, cerco una chiave main_flags , dato che sono lo sviluppatore originale, saprò che per tutti gli oggetti OutputExportData , ho bisogno di implementare questo tipo di array, ma sviluppatori che verranno e creare i loro nuovi oggetti da inserire nei miei flussi principali non lo sapranno, questo è un grosso problema: lo script PHP si romperà, poiché non riesce a trovare quella chiave nell'array.

Naturalmente posso inserirlo nei commenti e nella documentazione, ma non c'è un modo per digitare-suggerire ciò che mi aspetto che PHP inserisca / esca nell'interfaccia, come ad esempio:

interface OutputExportData {
    public function output() : array<'main_flags' => array, 'name' => string> {}
}

In modo che io possa forzare, attraverso il codice che chiunque sta implementando la mia interfaccia, a rispettare lo stesso schema di ritorno, quindi problemi come quello sopra indicato non succederanno?

Preoccupazioni:

  1. Capisco che nel mio foreach posso semplicemente controllare per questi chiavi e che l'interfaccia fornisce un contratto per il livello più alto flusso dell'oggetto (attraverso la denominazione delle funzioni dell'interfaccia da dove possiamo in qualche modo dire con precisione cosa stanno facendo), ma un'interfaccia non può anche riguardare come la sua implementazione le funzioni degli oggetti emettono / spostano le cose?
  2. Questo sembra sbagliato: questa chiave main_flags , devo solo usarla in questo foreach specifico e da nessun'altra parte, beh, naturalmente con il crescere del mio sistema, potrei usarlo in altri posti, ma per questi altri posti , sarà importante che le mie funzioni debbano rispettare quella struttura? Non ne sono così sicuro
  3. Che succede se la mia funzione output riscontra un errore fatale e non posso, in nessuna circostanza, generare il mio schema desiderato, non posso quindi fare:
interface OutputExportData {
    public function output() : boolean / array<'main_flags' => array, 'name' => string> {}
}

E alla fine, la domanda migliore, oltre a quella originale, è: dovrei farlo anche io?

Ho letto molto sulle interfacce, anche per altre lingue e qui c'è una citazione:

The functionality of a class does not depend on the interfaces it implements, the interface just provides a generic way of accessing the functionality.

Che è on-point, ma se fosse possibile fare ciò che voglio, allora la citazione cambierà in:

The functionality of a class does not depend on the interfaces it implements, the interface provides a way of accessing the functionality and dictates what that functionality's end goal is.

Che non suona come un'interfaccia.

    
posta coolpasta 17.11.2018 - 09:40
fonte

2 risposte

5

Come suggerito da Michael Borgwardt nei commenti, i modi PHP per descrivere una "struttura dati" sono classi e interfacce. Mostrerò un esempio con un'interfaccia aggiuntiva, ma potresti usare invece una classe.

Scrivi un'interfaccia per descrivere cosa deve essere restituito come output:

interface Output
{
    public function getMainFlags() : array;
    public function getName() : string;
}

Quindi modifica l'interfaccia OutputExportData per dichiarare che restituisce Output:

interface OutputExportData
{
    public function output() : Output; 
}

Ora qualsiasi classe che implementa OutputExportData sarà costretta a restituire un valore che è un'istanza di Output e il codice chiamante può essere certo che i metodi getMainFlags e getName esisteranno e restituiranno string e array.

    
risposta data 17.11.2018 - 14:05
fonte
1

Ciò che hai descivato è chiamato design per contratto . Il requisito che l'array restituito debba contenere una chiave "main_flags" è chiamato postcondition .

L'elemento di linguaggio interface (in PHP, o in altri linguaggi come Java o C #), tuttavia, non fornisce vincoli di tale semantica, le interfacce possono essere utilizzate solo per definire la parte sintattica di un contratto, non la parte semantica.

Per molte lingue, anche se non forniscono DBC "out of the box", esistono framework che possono aiutare a implementare precondizioni, postcondizioni e invarianti direttamente nel codice. Il link di Wikipedia sopra menziona alcuni framework per PHP (disclaimer: non li ho provati da solo, devi valutare da solo).

Senza un simile framework, il modo tipico per garantire una post-condizione è

  • scrivi esattamente nella documentazione dell'interfaccia di ciò che la funzione deve restituire

  • scrivi test unitari che controllano la post-condizione per ogni implementazione di interfaccia

  • aggiungi un codice di convalida alle chiamate della funzione di output che controllano la presenza della chiave e interrompe immediatamente lo script quando la post-condizione non è soddisfatta, oppure fallo gestirlo con garbo (ma senza mascherare il errore).

Se queste misure sono sufficienti, o se hai bisogno di un modo più rigoroso per controllare le post-condizioni e se vale la pena utilizzare un framework DBC, è qualcosa che devi scoprire da solo, per il tuo ambiente e la situazione.

    
risposta data 17.11.2018 - 10:12
fonte

Leggi altre domande sui tag