2010-10-04 8 views
5

tengo una clase como la siguiente:¿Cuál es la manera OO de refactorizar estas dos clases muy similares en 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); 
    } 
    } 
... 
} 

Voy a ser la implementación de una nueva clase llamada InterestsImagesStore en el que las únicas diferencias entre estas clases será el valor de $table, $dream_id será $interest_id y dream_id en SQL será interest_id.

Sé que hay una mejor manera de hacer esto, y estaré implementando clases similares en el futuro que tengan tan pequeñas diferencias.

¿Cuál es la mejor forma orientada a objetos para refactorizar mi código para evitar la duplicación y aumentar el mantenimiento?

+3

+1 para reconocer cuándo pedir ayuda * antes * crear un desastre descuidado que necesita ser refactorizado por otra persona – Zak

+0

@Zak Gracias. Intento codificar como si la persona que me sigue es un maníaco homicida. Y cuando no puedo entender la mayoría de lo que se dice sobre los libros de patrones, me imaginé que un ejemplo real y significativo me ayudaría a entenderlo. –

Respuesta

11

crear una clase ImagesStore de 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. 
} 

y crear DreamsImagesStore y InterestsImagesStore especializaciones:

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'); 
    } 
} 

el método original insertNewDreamImage puede cambiar el nombre a insertImage ya que es realmente más general que el original nombre sugiere

Tenga en cuenta que ImagesStore también se puede declarar abstract si desea evitar la instanciación directa de la misma.

Un enfoque alternativo que puede ser adoptada es la de no molestar a las clases derivadas de ImagesStore en absoluto y sólo una instancia directamente, haciendo que el __construct método public y decir que es como sigue:

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

Otro enfoque también podría ser para implementar un método de fábrica estático en ImagesStore.

+0

Gracias por esto. En 'insertImage()', ¿no quiere decir '$ this-> table' en lugar de' $ table'? Si no, ¿por qué? –

+3

+1, pero ¿es realmente necesario subclasificar con los valores codificados? ¿Qué tal crear una función de fábrica que tome el tipo de tienda como un param y devuelva una instancia apropiada de la tienda de imágenes? – Zak

+2

@Josh Smith: Cualquiera de los dos enfoques funciona. Esta implementación ilustra el acceso a estos campos a través de los métodos getter pero también se puede acceder directamente a los campos como '$ this-> table' y' $ this-> id_column' desde los métodos de la clase base.Los métodos en las clases derivadas tendrían que usar 'getTable' y' getIdColumn' en su lugar, ya que he declarado los campos 'private'. Para el código en la clase base, los métodos son correctos y se reducen al gusto. –

2

utilizando la clase ImagesStore creado por Richard Cook, esto también podría ocurrir:

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 incluso se podría conseguir más elegante y hacer algo como

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); 
} 

llamada de la siguiente manera

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

no puedo recordar si el nombre de la tabla vuelve a ser como tableName en la fila ... debería estar marcado ... – Zak

Cuestiones relacionadas