2010-10-04 1 views
5

Ho una classe come la seguente:Qual è il modo OO di refactoring di queste due classi molto simili in PHP?

class DreamsImagesStore 
{ 
    public $table = 'dreams_images'; 

    public function insertNewDreamImage($dream_id, $pid) 
    { 
    try { 
     $values = array($dream_id, $pid); 
     $sth = $this->dbh->prepare("INSERT INTO {$this->table} 
           (dream_id, pid) 
           VALUES (?, ?)"); 
     if($sth->execute($values)) { 
     return true; 
     } 
    } catch(PDOException $e) { 
     $this->errorLogger($e); 
    } 
    } 
... 
} 

ho intenzione di essere l'implementazione di una nuova classe chiamata InterestsImagesStore in cui le uniche differenze tra queste classi avranno il valore di $table, $dream_id sarà $interest_id, e dream_id in SQL sarà interest_id.

So che c'è un modo migliore per farlo e in futuro implementerò classi simili con differenze così piccole.

Qual è il miglior modo orientato agli oggetti per rifattorizzare il mio codice per evitare la duplicazione e aumentare la manutenibilità?

+3

+1 per riconoscere quando chiedere aiuto * prima * creare un disordine sciatto che deve essere rifatto da qualcun altro – Zak

+0

@ Zak Grazie. Provo a codificare come se la persona che veniva dopo di me fosse un maniaco omicida. E quando non posso per la vita di me capire la maggior parte di ciò che viene detto libri di modelli, ho pensato che un esempio significativo della vita reale mi avrebbe aiutato a capirlo. –

risposta

11

creare una classe ImagesStore di base:

class ImagesStore 
{ 
    // See comments about accessors below. 
    private $table; 
    private $id_column; 

    public function insertImage($id, $pid) { 
    try { 
     $values = array($id, $pid); 
     $table = $this->getTable(); 
     $id_column = $this->getIdColumn(); 
     $sth = $this->dbh->prepare("INSERT INTO {$table} ($id_column, pid) VALUES (?, ?)"); 
     if ($sth->execute($values)) { 
     return true; 
     } 
    } 
    catch (PDOException $e) { 
     $this->errorLogger($e); 
    } 
    } 

    protected function __construct($table, $id_column) { 
    $this->table = $table; 
    $this->id_column = $id_column; 
    } 

    // These accessors are only required if derived classes need access 
    // to $table and $id_column. Declaring the fields "private" and providing 
    // "protected" getters like this prevents the derived classes from 
    // modifying these values which might be a desirable property of these 
    // fields. 
    protected function getTable() {return $this->table;} 
    protected function getIdColumn() {return $this->id_column;} 

    // More implementation here... 
    // Initialize $dbh to something etc. 
    // Provide "errorLogger" method etc. 
} 

e creare DreamsImagesStore e InterestsImagesStore specializzazioni:

class DreamsImagesStore extends ImagesStore { 
    public function __construct() { 
    parent::__construct('dreams_images', 'dream_id'); 
    } 
} 

class InterestsImagesStore extends ImagesStore { 
    public function __construct() { 
    parent::__construct('interests_images', 'interest_id'); 
    } 
} 

il metodo originale insertNewDreamImage possono essere rinominati per insertImage come realmente è più generale di quella originale nome suggerisce.

Si noti che ImagesStore può anche essere dichiarato abstract se si desidera impedire l'istanza diretta di esso.

Un approccio alternativo che può essere adottato è quello di non preoccuparsi classi derivanti da ImagesStore a tutti e soli istanziare direttamente rendendo il metodo public__construct e chiamando come segue:

$dreamsImagesStore = new ImagesStore("dreams_images", "dream_id"); 

altro approccio potrebbe anche essere implementare un metodo factory static in ImagesStore.

+0

Grazie per questo. In 'insertImage()', non intendi '$ this-> table' piuttosto che' $ table'? Se no, perché? –

+3

+1, ma è davvero necessario anche creare sottoclassi con i valori codificati? Che dire della creazione di una funzione factory che prende il tipo di negozio come parametro e restituisce un'istanza appropriata dell'archivio immagini? – Zak

+2

@Josh Smith: O l'approccio funziona. Questa implementazione illustra l'accesso a questi campi tramite i metodi getter, ma è anche possibile accedere direttamente ai campi come $ this-> table' e $ this-> id_column' dai metodi della classe base.I metodi nelle classi derivate dovrebbero usare 'getTable' e' getIdColumn' invece di aver dichiarato i campi 'private'. Per il codice nei metodi della classe base è corretto e viene giù per il gusto. –

2

utilizzando la classe ImagesStore creato da Richard Cook, questo potrebbe anche accadere:

function FactoryLoadImageStore($imageStoreType) 
{ 
    switch($imageStoreType) 
    { 
     case "Interests": 
      return new ImageStore('interests_images', 'interest_id'); 
     case "Dreams": 
      return new ImageStore('dreams_images', 'dreams_id'); 
     default: 
      throw new Exception("ImageStore type $imageStoreType not found") 
; } 

} 

o si potrebbe anche ottenere fantasiosi e fare qualcosa di simile

function FactoryLoadImageStore($imageStoreType) 
{ 
    $tableName = $imageStoreType . 's_images'; 
    $idColumnName = $imageStoreType . 's_id'; 
    $tableExists = false; 
    $sql= "Show tables like '$tableName' "; 
    foreach ($this->dbh->query($sql) as $row) 
    { 
     if ($row['tableName'] == $tableName) 
     { 
      $tableExists = true; 
      break; 
     } 
    } 
    if(!$tableExists) 
    { 
     throw new Exception ("No matching table exists for the requested image store $imageStoreType"); 
    } 

    return new ImageStore($tableName, $idColumnName); 
} 

chiamata come segue

$dreamImageStore = ImageStore::FactoryLoadImageStore('dream'); 
+0

non ricordo se il nome della tabella ritorna come tableName nella riga .. deve essere controllato ... – Zak