Principio ASCIUTO in buone pratiche?

11

Sto cercando di seguire il principio ASCIUTTO nella mia programmazione più che posso. Recentemente ho imparato modelli di progettazione in OOP e ho finito per ripetermi abbastanza.

Ho creato un pattern di repository insieme a modelli Factory e Gateway per gestire la mia persistenza. Sto utilizzando un database nella mia applicazione ma ciò non dovrebbe avere importanza dal momento che dovrei essere in grado di sostituire il gateway e passare a un altro tipo di persistenza, se lo desidera.

Il problema che ho creato per me è che creo gli stessi oggetti per il numero di tabelle che ho. Ad esempio, questi saranno gli oggetti di cui ho bisogno per gestire una tabella comments .

class Comment extends Model {

    protected $id;
    protected $author;
    protected $text;
    protected $date;
}

class CommentFactory implements iFactory {

    public function createFrom(array $data) {
        return new Comment($data);
    }
}

class CommentGateway implements iGateway {

    protected $db;

    public function __construct(\Database $db) {
        $this->db = $db;
    }

    public function persist($data) {

        if(isset($data['id'])) {
            $sql = 'UPDATE comments SET author = ?, text = ?, date = ? WHERE id = ?';
            $this->db->prepare($sql)->execute($data['author'], $data['text'], $data['date'], $data['id']);
        } else {
            $sql = 'INSERT INTO comments (author, text, date) VALUES (?, ?, ?)';
            $this->db->prepare($sql)->execute($data['author'], $data['text'], $data['date']);
        }
    }

    public function retrieve($id) {

        $sql = 'SELECT * FROM comments WHERE id = ?';
        return $this->db->prepare($sql)->execute($id)->fetch();
    }

    public function delete($id) {

        $sql = 'DELETE FROM comments WHERE id = ?';
        return $this->db->prepare($sql)->execute($id)->fetch();
    }
}

class CommentRepository {

    protected $gateway;
    protected $factory;

    public function __construct(iFactory $f, iGateway $g) {
        $this->gateway = $g;
        $this->factory = $f;
    }

    public function get($id) {

        $data = $this->gateway->retrieve($id);
        return $this->factory->createFrom($data);
    }

    public function add(Comment $comment) {

        $data = $comment->toArray();
        return $this->gateway->persist($data);
    }
}

Quindi il mio controller sembra

class Comment {

    public function view($id) {

        $gateway = new CommentGateway(Database::connection());
        $factory = new CommentFactory();
        $repo = new CommentRepository($factory, $gateway);

        return Response::view('comment/view', $repo->get($id));
    }
}

Quindi pensavo di usare correttamente i modelli di progettazione e di mantenere buone pratiche, ma il problema con questa cosa è che quando aggiungo una nuova tabella, devo creare le stesse classi solo con altri nomi. Ciò solleva in me il sospetto che stia facendo qualcosa di sbagliato.

Ho pensato ad una soluzione in cui invece di interfacce avevo classi astratte che usando il nome della classe capivano la tabella che dovevano manipolare ma che non sembrava la cosa giusta da fare, e se decidessi di passare ad un archiviazione di file o memcache dove non ci sono tabelle.

Mi sto avvicinando correttamente a questo, o c'è una prospettiva diversa che dovrei guardare?

    
posta Emilio Rodrigues 22.08.2015 - 20:41
fonte

2 risposte

12

Il problema che stai affrontando è abbastanza fondamentale.

Ho riscontrato lo stesso problema quando lavoravo per un'azienda fatto una grande applicazione J2EE che consisteva di diverse centinaia di pagine web e oltre un milione e mezzo di linee di codice Java. Questo codice utilizza ORM (JPA) per la persistenza.

Questo problema peggiora quando si utilizzano tecnologie di terze parti in ogni livello dell'architettura e tutte le tecnologie richiedono la propria rappresentazione dei dati.

Il problema non può essere risolto a livello del linguaggio di programmazione che si sta utilizzando. Usare i pattern è buono ma, come vedete, causa la ripetizione del codice (o più precisamente, la ripetizione dei disegni).

Per come la vedo ci sono solo 3 possibili soluzioni. In pratica, queste soluzioni arrivano allo stesso modo.

Soluzione 1: utilizzare un altro framework di persistenza che permetta di indicare solo cosa deve essere persistente. Probabilmente c'è una tale struttura in giro. Il problema con questo approccio è che è piuttosto ingenuo perché non tutti i modelli saranno correlati alla persistenza. Inoltre, si desidera utilizzare i pattern per il codice dell'interfaccia utente in modo da avere quindi bisogno di un framework GUI che possa riutilizzare le rappresentazioni dei dati del framework di persistenza scelto. Se non è possibile riutilizzarli, è necessario scrivere il codice della piastra della caldaia per collegare le rappresentazioni dei dati della struttura della GUI e il framework di persistenza .. e questo è di nuovo contrario al principio DRY.

Soluzione 2: usa un altro linguaggio di programmazione più potente che include costrutti che ti consentono di esprimere il design ripetitivo in modo da poter riutilizzare il codice di progettazione. Questa probabilmente non è un'opzione per te, ma supponiamo per un momento che lo sia. Poi di nuovo quando si inizia a creare un'interfaccia utente sopra il livello di persistenza, si vorrà che il linguaggio sia di nuovo abbastanza potente da supportare la creazione della GUI senza dover scrivere il codice della piastra della caldaia. È improbabile che esista un linguaggio abbastanza potente per fare ciò che si desidera dal momento che la maggior parte delle lingue si basa su framework di terze parti per la costruzione di GUI che richiedono la propria rappresentazione dei dati per funzionare.

Soluzione 3: Automatizza la ripetizione del codice e del design usando una qualche forma di generazione del codice. La vostra preoccupazione consiste nel dover codificare a mano ripetizioni di modelli e disegni poiché codice / design ripetitivo codificante a mano violano il principio DRY. Oggigiorno ci sono framework di generatori di codice molto potenti là fuori. Esistono persino "banchi di lavoro per la lingua" che consentono di creare rapidamente il proprio linguaggio di programmazione (mezza giornata in cui non si ha esperienza) e generare qualsiasi codice (PHP / Java / SQL - qualsiasi file di testo pensabile) utilizzando tale linguaggio. Ho esperienza con XText ma MetaEdit e MPS sembrano andare bene. Ti consiglio caldamente per controllare uno di questi banchi di lavoro linguistici. Per me è stata l'esperienza più liberatoria della mia vita professionale.

Usando Xtext puoi fare in modo che la tua macchina generi il codice ripetitivo. Xtext genera anche un editor di evidenziazione della sintassi per te con il completamento del codice per le tue specifiche di lingua. Da quel punto devi semplicemente prendere il gateway e la classe di fabbrica e trasformarli in modelli di codice perforandoli con buchi. Li date al vostro generatore (che viene chiamato da un parser della vostra lingua che è anche completamente generato da Xtext) e il generatore riempirà i buchi nei vostri modelli. Il risultato è codice generato. Da quel momento in poi è possibile eliminare qualsiasi ripetizione di codice ovunque (codice di persistenza del codice GUI, ecc.).

    
risposta data 23.08.2015 - 11:47
fonte
8

Il problema che stai affrontando è vecchio: il codice per gli oggetti persistenti è spesso simile per ogni classe, è semplicemente un codice boilerplate. Ecco perché alcune persone intelligenti hanno inventato Object Relational Mappers - risolvono esattamente questo problema. Vedi questo precedente post SO per un elenco di ORM per PHP.

Quando gli ORM esistenti non subiscono le tue esigenze, c'è anche un'alternativa: puoi scrivere il tuo generatore di codice, che richiede una meta descrizione dei tuoi oggetti per persistere e genera la parte ripetitiva del codice da quella. Questo in realtà non è troppo difficile, l'ho fatto in passato per alcuni linguaggi di programmazione diversi, sono sicuro che sarà anche possibile implementare queste cose anche in PHP.

    
risposta data 23.08.2015 - 08:55
fonte

Leggi altre domande sui tag