Riutilizzare un'interfaccia o un metodo di concatenazione

3

Sto imparando sulla programmazione di un'interfaccia nella programmazione OOP. Capisco che dovresti codificare un'interfaccia in modo da poterla "scambiare".

Tuttavia, ho una situazione in cui sento di poter riutilizzare nuovamente un'interfaccia piuttosto che semplicemente sostituirla.

interface GetFile
{
    public function getFile();
}

class GetFileFromUrl implements GetFile
{
    public function __construct($url, $destination_path) {...}

    public function getFile()
    {
        // download file from url and return path of file

        return 'file_path';
    }
}

class GetFileFromZip implements GetFile
{
    public function __construct($path, $extraction_path) {...}

    public function getFile()
    {
        // extract file from zip and return path of file

        return 'file_path';
    }
}

class DoStuff
{
    // coding to an interface
    public function doStuff(File $file) {
        $do_stuff = $file->getFile();
        //do stuff with file
    }
}

Ho pensato che questo avrebbe permesso la massima flessibilità nel riuso del codice in futuro, dato che posso estendere ulteriormente l'interfaccia

class GetFileFromDirectory implements GetFile(...)
class GetFileFromTar implements GetFile(...)

I sento questo un approccio abbastanza buono in quanto posso passare qualsiasi implementazione di GetFile a DoStuff e eseguirà readFile nel metodo doStuff() .

La mia domanda che sto cercando di porre è come posso "concatenare" queste classi che implementano GetFile insieme?

vale a dire. GetFileFromUrl poi GetFileFromZip per ottenere un file zip da un URL e decomprimerlo?

Quindi usare solo uno sarebbe

$file_from_url = new GetFileFromUrl($url_path, $destination_path);
$do_stuff      = (new DoStuff)->doStuff($file_from_url);

Ma "concatenando" due che sto facendo attualmente;

$zip_file_path = (new GetFileFromUrl($url_path, $destination_path))->getFile();
$file_from_zip = new GetFileFromZip($zip_file_path, $destination_path);
$do_stuff      = (new DoStuff)->doStuff($zip_from_tar);

C'è un modo migliore / più pulito per farlo?

Grazie a thepacker , il mio pensiero rivisto è quello di separare la funzionalità in un'unica interfaccia che ottiene un file da qualche parte e un'altra interfaccia che fa qualcosa al file;

interface GetFile()
interface ConvertFile()

Quindi potrei fare

class GetFileFromUrl implements GetFile {...}

class ConvertFileFromZip implements ConvertFile {
    // coding to an interface
    public function __construct(GetFile $from) {...}
}

$zip_from_url = new GetFileFromUrl($url, $destination);
$file_path    = new ConvertFileFromZip($zip_from_url);

Voglio solo confermare che questa è la linea di condotta corretta in questo caso.

    
posta myol 03.10.2015 - 18:07
fonte

1 risposta

4

Bene, forse posso illuminarti leggermente. Non stai scambiando l'interfaccia, ma l'implementazione.

GetFile fileGetter=new GetFileFromUrl(...);
// but you can use every other filegetter instead... 
fileGetter->getFile();

Pensa a questa classe:

class ConfigParser 
{
    public function readConfig(GetFile fileGetter)
    {
        ...
        currentFile = fileGetter->getFile()
        ...
    }
}  

Ogni volta che ritieni di aver bisogno di qualcosa per ottenere file da qualsiasi luogo, puoi implementare un metodo e trasferire la strategia su come ottenere i file da Zip o tar, o URL o qualsiasi strategia ti piaccia all'istanza che richiede quella . Considera questo:

ConfigParser configParser...;
configParser->readConfig(fileGetter)

Poiché ConfigParser non si preoccupa di come accedere ai file (utilizza invece l'interfaccia), può essere utilizzato per leggere e analizzare i file di configurazione da qualsiasi luogo.

Puoi anche implementare una classe usando un'altra istanza di GetFile. Come un FileGetter che usa gli URL, ma può memorizzare nella cache i file.

class CachingFileGetter implements GetFile {
  GetFile wrappedGetFileStrategy;
  ....

  public function __construct(GetFile nonCached) {
      wrappedGetFileStrategy=nonCached;
      ....
    }

  public function getFile() {
    if(isCached())
    {
       [do your caching magic here]
    }
    else
    {
       wrappedGetFileStrategy->getFile();
       ...
    }
  }
}

Questo è ciò che le interfacce potrebbero essere per. Stai separando le tue preoccupazioni come l'implementazione di un FileGetter memorizzato nella cache, ma non ti importa da dove provenga quel file - Invece stai usando l'astratta GetFile-thing. Leggi di Liskov-Sostituzione - Principle the L in SOLID.

[Aggiornamento]

In risposta al tuo aggiornamento. Non utilizzare il concatenamento di metodi (allo oltre il tuo codice) poiché hai un modo per nascondere tale comportamento. Puoi sostituire l'idea di una cache semplicemente con l'idea di un file system virtuale (VFS), quindi non devi copiare ovunque:

$zip_file_path = (new GetFileFromUrl($url_path, $destination_path))->getFile();
$file_from_zip = new GetFileFromZip($zip_file_path, $destination_path);

L'idea alla base di ciò è che il tuo codice non ha bisogno di sapere che il file è un file zip in un url, è solo una risorsa file. C'è solo un posto dove lasciare queste informazioni, che stai usando i file Zip su URL, nella configurazione. Non copiare e incollare tali "soluzioni" (conoscenza della tua configurazione) su tutto il tuo codice - Se cambi idea di utilizzare il nuovissimo FOO istead di zip, o BAR invece di URL, fissi costantemente il tuo codice ovunque. Implementa un VFS di classe che nasconde il comportamento del concatenamento del metodo e utilizza URL-FileGet e Zip-FileGet all'interno. E poi usa

(new DoStuff)->doStuff($VFS) instead .
    
risposta data 03.10.2015 - 19:55
fonte

Leggi altre domande sui tag