Posso risolvere un problema con SRP, SOC che impedisce l'inizializzazione dell'oggetto tramite costruttore?

3

In OOP, posso popolare (inizializzare) un oggetto usando l'iniezione del costruttore al momento della creazione dell'oggetto, o dell'iniezione setter, dopo il tempo di creazione.

Mi piace l'idea di popolare l'oggetto al momento della creazione tramite il costruttore, per i parametri dell'oggetto che sono essenziali per l'oggetto. E usa i metodi setter per cose che non sono essenziali o secondarie.

E tutto andava bene.

Fino a quando ho scoperto DataMapper pattern. Nel modello I

  • carica i dati dal database (attenzione ai dati)
  • mappare le colonne dello schema ai parametri dell'oggetto (problemi di mappatura)
  • crea / aggiorna / elimina / restituisce l'oggetto al suo chiamante (gestione della gestione degli oggetti)

Sono abbastanza contento che DataMapper sia essenzialmente il gestore per l'oggetto, creando quindi l'oggetto, caricando i dati e quindi popolando quell'oggetto. Se tutte e 3 le preoccupazioni precedenti condividono la stessa funzione, non vi è alcun problema in quanto sembrano integrarsi perfettamente insieme.

problema

Il problema sorge quando inizio a separare le preoccupazioni. Vale a dire, la preoccupazione di mappatura. La preoccupazione di mappatura dovrebbe riguardare solo se prendere i nomi delle colonne dello schema DB e copiare i dati dall'oggetto risorsa DB sulle variabili dei parametri del campo dati del nuovo oggetto. Per me si traduce in questo

  • accetta un oggetto creato come parametro di input
  • riempi l'oggetto dallo schema DB tramite il mapping (utilizzando setter per private vars o direttamente se vars sono pubblici)
  • restituisce l'oggetto compilato al suo chiamante.

Penso che la preoccupazione per la mappatura non dovrebbe essere la creazione dell'oggetto. Ciò significa che la capacità di mappatura non può utilizzare l'inizializzazione del costruttore .

Domanda

Che cosa posso fare per utilizzare la preoccupazione di associazione separata e utilizzare ancora l'inizializzazione del costruttore?

Possibili soluzioni alternative

  • Utilizza solo i metodi di inizializzazione setter [abbandona l'inizializzazione del costruttore anche quando lo desideri]
  • racchiudono due diversi problemi di gestione dell'oggetto (ovvero la creazione [significa capacità di inizializzarlo tramite il costruttore]) e il mapping schema-oggetto all'interno della stessa classe. [abbandona SRP]

Esempio di codice

Per chiarezza - vedi commenti

class ProductDataMapper
{
    public function dataConcern($id)
    {
        $sql = "SELECT model as name FROM product where id = {$id}";
        $result = db_query($sql);
        $data = db_fetch_array($result);
        return $data;
    }

    public function mappingConcern($data)
    {
        //schema to property mapping (to separate concerns)
        $parameters = array();
        $parameters['name'] = $data['schema_column_name_in_db'];

        //In order to use constructor initialization, mapping concern
        //has to be the place to *create* the product (using new)
        //my question deals with separating 'object creation' from 'object mapping'

        //init via constructor - this is the *undesirable* concern for a mapper
        $product = new Product($parameters);

        //init via setters
        foreach ($data as $key => $value) {
            $setter = 'set' . ucfirst($key);
            $product->$setter($value);
        }
        return $product;
    }

    public function getProduct($productId)
    {
        $data = $this->dataConcern($productId);
        $product = $this->mappingConcern($data);
        return $product;
    }
}

Aggiorna

Sto pensando di poter separare la creazione dell'oggetto con la mappatura in una preoccupazione separata.

Questa preoccupazione sarà Object-Creation-and-Schema-Mapping-To-Object-via-Constructor .

Vale a dire, ancora non perfetto, perché ci saranno due problemi di mapper

  • mapper che crea e inizializza l'oggetto tramite il costruttore
  • mapper che utilizza i setter per popolare l'oggetto
posta Dennis 01.07.2015 - 23:17
fonte

1 risposta

4

Stai sfruttando eccessivamente SRP. Diamo un'occhiata alla versione di creazione e inizializzazione di mappingConcern :

public function mappingConcern($data)
{
    $parameters = $data;

    $product = new Product($parameters);

    return $product;
}

$parameters è uguale a $data e $product viene restituito immediatamente, quindi può essere tradotto in:

public function mappingConcern($data)
{
    return new Product($data);
}

Questa è la tua funzione! Chiamare un'altra funzione (il costruttore)! Nient'altro - nessuna logica per scegliere la funzione, nessuna preparazione di argomenti, nessuna analisi di valore restituito, niente di niente - basta chiamare un'altra funzione, passando l'argomento così com'è.

Perché non puoi farlo direttamente da getProduct ?

public function getProduct($productId)
{
    $data = $this->dataConcern($productId);
    return new Product($data);
}

Quando lo fai in questo modo, la domanda di costruttore vs setter sembra sciocca, giusto? Quindi, anche se hai bisogno di una funzione mappingConcern (per polimorfismo o altro), perché non può farlo questo semplice comando?

Penso che il tuo problema sia che ti stai immergendo troppo in profondità quando cerchi di separare responsabilità e preoccupazioni. SRP e SoC si riferiscono all'interfaccia, non all'implementazione (l'implementazione ha altre regole per separare le cose). Cioè, quando descrivi cosa una funzione dovrebbe fare solo una cosa, ma quando descrivi come lo fa puoi descrivere più cose. Ciò che mappingConcern non è " crea un oggetto e mappa i dati ad esso " - questa è l'implementazione. Quello che mappingConcern fa è " converti la% grezza$data in un oggetto ". Questa è una cosa, ed è conforme a SRP e SoC.

    
risposta data 02.07.2015 - 00:55
fonte