Aggiunta di campi alla classe in fase di esecuzione - modello di progettazione

15

Immagina che i tuoi clienti desiderino avere la possibilità di aggiungere nuove proprietà (ad es. colore) al prodotto nel loro eshop nel loro CMS.

Invece di avere proprietà come campi:

class Car extends Product {
   protected String type;
   protected int seats;
}

Probabilmente finirai per fare qualcosa del tipo:

class Product {
   protected String productName;
   protected Map<String, Property> properties;
}

class Property {
   protected String name;
   protected String value;
}

Cioè, creando il proprio sistema di tipi in cima a quello esistente. Mi sembra che questo potrebbe essere visto come la creazione di langauge specifico del dominio, o non potrebbe?

Questo approccio è un modello di design noto? Risolvi il problema in modo diverso? So che ci sono linguaggi in cui posso aggiungere un campo in runtime, ma che dire del database? Preferiresti aggiungere / modificare colonne o usato qualcosa come mostrato sopra?

Grazie per il tuo tempo :).

    
posta Filip 18.04.2014 - 02:02
fonte

4 risposte

4

Congratulazioni! Hai appena circumnavigato il linguaggio del sistema di programmazione / tipo, arrivando dall'altra parte del mondo da dove sei partito. Sei appena atterrato al confine del linguaggio dinamico / oggetto basato su prototipi!

Molti linguaggi dinamici (ad es. JavaScript, PHP, Python) consentono di estendere o modificare le proprietà dell'oggetto in fase di runtime.

La forma estrema di questo è un linguaggio basato su prototipi come Self o JavaScript. Essi non ho lezioni, in senso stretto. Puoi fare cose che assomigliano alla programmazione orientata agli oggetti e basata sull'oggetto con l'ereditarietà, ma le regole sono notevolmente rilassate rispetto a linguaggi basati su classi più nettamente definiti come Java e C #.

Langauges come PHP e Python vivono in mezzo. Hanno sistemi regolari e idiomatici basati su classi. Ma gli attributi degli oggetti possono essere aggiunti, modificati o eliminati in fase di esecuzione, anche se con alcune restrizioni (come "tranne i tipi predefiniti") che non trovi in JavaScript.

Il grande compromesso per questo dinamismo è la performance. Dimentica quanto tipicamente o debolmente è stata scritta la lingua, o quanto bene può essere compilato fino al codice macchina. Gli oggetti dinamici devono essere rappresentati come mappe / dizionari flessibili, piuttosto che semplici strutture. Ciò aggiunge un sovraccarico a ogni accesso agli oggetti. Alcuni programmi fanno di tutto per ridurre questo overhead (ad esempio con l'assegnazione di phantom kwarg e le classi basate su slot in Python), ma il sovraccarico in più è di solito solo pari per il corso e il prezzo di ammissione.

Tornando al tuo design, stai innestando la possibilità di avere proprietà dinamiche su un sottoinsieme delle tue classi. Un Product può avere attributi variabili; presumibilmente un Invoice o un Order sarebbe e non potrebbe. Non è un brutto modo di andare. Ti dà la flessibilità di avere variazioni dove ne hai bisogno, pur rimanendo in un sistema di linguaggio e tipo rigoroso e disciplinato. Sul lato negativo, sei responsabile della gestione di quelle proprietà flessibili e probabilmente dovrai farlo attraverso meccanismi che sembrano leggermente diversi da altri attributi nativi. p.prop('tensile_strength') anziché p.tensile_strength , ad esempio, e p.set_prop('tensile_strength', 104.4) anziché p.tensile_strength = 104.4 . Ma ho lavorato con e costruito molti programmi in Pascal, Ada, C, Java e persino in linguaggi dinamici che usavano esattamente tale accesso getter-setter per tipi di attributi non standard; l'approccio è chiaramente attuabile.

Per inciso, questa tensione tra tipi statici e un mondo molto vario è estremamente comune. Un problema analogo si riscontra spesso nella progettazione dello schema del database, in particolare per gli archivi dati relazionali e pre-relazionali. A volte viene affrontato creando "super-righe" che contengono abbastanza flessibilità per contenere o definire l'unione di tutte le varianti immaginate, quindi riempire tutti i dati che arrivano in quei campi. La tabella wp_posts di WordPress , ad esempio, ha campi come comment_count , ping_status , post_parent e post_date_gmt che sono solo interessanti in alcune circostanze, e che nella pratica spesso diventano vuote. Un altro approccio è una tabella molto normalizzata come wp_options , proprio come la tua classe Property . Mentre richiede una gestione più esplicita, gli elementi in esso contenuti sono raramente vuoti. I database orientati agli oggetti e ai documenti (ad esempio MongoDB) spesso hanno un tempo più semplice per gestire le opzioni di modifica, in quanto possono creare e impostare gli attributi praticamente a volontà.

    
risposta data 16.08.2014 - 16:50
fonte
0

Mi piace la domanda, i miei due centesimi:

I tuoi due approcci sono radicalmente diversi:

  • Il primo è OO an strongmente digitato - ma non estensibile
  • Il secondo è scritto debolmente (la stringa incapsula qualsiasi cosa)

In C ++, molti userebbero una std :: map di boost :: variante per ottenere un mix di entrambi.

Disgression: Nota che alcune lingue, come C #, consentono la creazione dinamica di tipi. Quale potrebbe essere una buona soluzione per il problema generale di aggiungere membri dinamicamente. Tuttavia, i tipi di "modifica / aggiunta" dopo la compilazione corrompono il sistema di tipo stesso e rendono quasi inutilizzabili i tipi "modificati" (ad esempio, come accederesti a tali proprietà aggiunte, poiché non sai nemmeno che esistono? L'unico modo ragionevole sarebbe sii una riflessione sistematica su ogni oggetto ... finendo con un linguaggio dinamico puro _ puoi fare riferimento alla parola chiave 'dinamica' .NET.

    
risposta data 18.04.2014 - 05:12
fonte
0

Creare un tipo in fase di esecuzione suona molto più complicato della semplice creazione di un livello di astrazione. È molto comune creare un'astrazione per disaccoppiare i sistemi.

Fammi mostrare un esempio dalla mia pratica. Scambio di Mosca ha il nucleo commerciale chiamato Plaza2 con l'API del trader. I commercianti scrivono i loro programmi per lavorare con i dati finanziari. Il problema è che questi dati sono molto grandi, complessi e altamente esposti ai cambiamenti. Può cambiare dopo l'introduzione di un nuovo prodotto finanziario o il cambio di liquidazione. La natura dei futuri cambiamenti non può essere prevista. Letteralmente, può cambiare ogni giorno e i programmatori poveri dovrebbero modificare il codice e rilasciare una nuova versione, e gli operatori arrabbiati dovrebbero rivedere i loro sistemi.

La decisione ovvia è quella di nascondere l'inferno finanziario dietro un'astrazione. Hanno usato l'astrazione delle tabelle SQL ben note. La maggior parte del loro core può funzionare con qualsiasi schema valido, lo stesso del software del trader può analizzare in modo dinamico lo schema e scoprire se è compatibile con il loro sistema.

Tornando al tuo esempio, è normale creare un linguaggio astratto di fronte ad una logica. Potrebbe essere "proprietà", "tabella", "messaggio" o anche linguaggio umano, ma prestare attenzione agli svantaggi di questo approccio:

  • Più codice per l'analisi e la convalida (e più tempo fuori rotta). Tutto ciò che il compilatore ha fatto per te con la digitazione statica devi fare in fase di esecuzione.
  • Più documentazione di messaggi, tabelle o altre primitive. Tutti i la complessità passa dal codice a una sorta di schema o standard. Qui è un esempio di schema infernale finanziario sopra menzionato: link (decine di pagine di tabelle)
risposta data 23.05.2014 - 12:29
fonte
0

Is this approach a known design pattern?

In XML e HTML, questi sarebbero gli attributi di un nodo / elemento. Li ho anche sentiti chiamare proprietà estese, coppie nome / valore e parametri.

Would you solve the problem differently?

Ecco come risolverei il problema, sì.

I know there are languages where I can add a field in runtime, but what about database?

Un database sarebbe come Java, in un certo senso. In pesudo-sql:

TABLE products
(
    product_name VARCHAR(50),
    product_id INTEGER AUTOINCREMENT
)

TABLE attributes
(
    product_id INTEGER,
    name VARCHAR(50),
    value VARCHAR(2000)
)

Questo corrisponderebbe a Java

class Product {
   protected String productName;
   protected Map<String, String> properties;
}

Si noti che non è necessaria una classe Property, in quanto la Mappa memorizza il nome come chiave.

Would you rather add/alter columns or used someting as shown above?

Ho provato la colonna Aggiungi / Modifica, ed è stato un incubo. Può essere fatto, ma le cose continuavano a non essere sincronizzate e non ho mai funzionato bene. La struttura della tabella che ho delineato sopra ha avuto molto più successo. Se devi eseguire ricerche e ordinare per, considera l'utilizzo di una tabella di attributi per ciascun tipo di dati ( date_attributes , currency_attributes , ecc.) O aggiungi alcune proprietà come colonne del vecchio database valide nella tabella prodotti. I report sono spesso molto più facili da scrivere su colonne del database e sotto tabelle.

    
risposta data 09.07.2014 - 06:39
fonte

Leggi altre domande sui tag