Aggiunta di campo 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) ai prodotti nel loro negozio nel loro CMS.

Invece di avere proprietà come campi:

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

Probabilmente finiresti 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 sopra a quello esistente. Mi sembra che questo potrebbe essere visto come la creazione di lingue specifiche per il dominio o no?

Questo approccio è un modello di design noto? Vuoi risolvere il problema in modo diverso? So che ci sono lingue in cui posso aggiungere un campo in fase di esecuzione, ma per quanto riguarda il database? Preferiresti aggiungere / modificare colonne o utilizzare qualcosa come mostrato sopra?

Grazie per il tuo tempo :).



Non sei sicuro di quale lingua stai usando, ma se è C # potresti usare un tipo dinamico che fondamentalmente memorizza un KVP in un dizionario come quello che stai facendo sui prodotti e ti consente di limitarti a puntare sulle proprietà senza doverle aggiungere direttamente a la collezione come collezione. Non avrai una digitazione forte però. So che hai chiesto un modello di design ma non credo che avresti bisogno di qualcosa di complesso per usarli. msdn.microsoft.com/en-us/magazine/gg598922.aspx
Tony,

Tony: Sto usando Java qui, ma lo considero pseudo coude :). C # mi permetterebbe di persistere quell'oggetto dinamico nel database? Ne dubito, poiché databse deve conoscere la struttura dei dati in anticipo.
Filip,

C'è lo State Desing Pattern a-la Gang-of-Four, che fa sembrare che un oggetto cambi il suo tipo o classe in fase di esecuzione. Altre alternative sono Observer Design Pattern o Proxy Design Pattern
Nikos M.

1
Perché non usare solo il tipo di dati della mappa? Nel DB può essere rappresentato come {id} + {id, chiave, valore}, se non si richiede una prestazione.
Ombre nella pioggia,

Risposte:


4

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

Molti linguaggi dinamici (ad esempio JavaScript, PHP, Python) consentono di estendere o modificare le proprietà degli oggetti in fase di esecuzione.

La forma estrema di questo è un linguaggio basato su prototipo come Self o JavaScript. Non hanno lezioni, a rigor di termini. Puoi fare cose che sembrano una programmazione basata su classi, orientata agli oggetti con ereditarietà, ma le regole sono molto più rilassate rispetto a linguaggi più definiti e basati su classi come Java e C #.

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

Il grande compromesso per questo dinamismo è la prestazione. Dimentica quanto fortemente o debolmente è scritta la lingua o quanto bene può essere compilata fino al codice macchina. Gli oggetti dinamici devono essere rappresentati come mappe / dizionari flessibili, piuttosto che semplici strutture. Ciò aggiunge sovraccarico ad ogni accesso agli oggetti. Alcuni programmi fanno di tutto per ridurre questo sovraccarico (ad es. Con l'assegnazione fantasma di kwarg e le classi basate su slot in Python), ma l'overhead aggiuntivo di solito è solo alla pari per il corso e il prezzo dell'ammissione.

Tornando al tuo design, stai innestando la possibilità di avere proprietà dinamiche su un sottoinsieme delle tue classi. A Productpuò avere attributi variabili; presumibilmente un Invoiceo un Ordersarebbe e non potrebbe. Non è un brutto modo di andare. Ti dà la flessibilità di avere variazioni dove ne hai bisogno, pur rimanendo in un linguaggio rigoroso e disciplinato e un sistema di tipi. Sul lato negativo, sei responsabile della gestione di quelle proprietà flessibili e probabilmente dovrai farlo attraverso meccanismi che sembrano leggermente diversi dagli altri attributi nativi. p.prop('tensile_strength')piuttosto che p.tensile_strength, per esempio, e p.set_prop('tensile_strength', 104.4)piuttosto chep.tensile_strength = 104.4. Ma ho lavorato e creato molti programmi in Pascal, Ada, C, Java e persino in linguaggi dinamici che utilizzavano esattamente tale accesso getter-setter per tipi di attributi non standard; l'approccio è chiaramente praticabile.

A proposito, questa tensione tra tipi statici e un mondo molto vario è estremamente comune. Un problema analogo si riscontra spesso durante la progettazione dello schema del database, in particolare per gli archivi di 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. Il WordPress wp_poststavolo , per esempio, ha campi come comment_count, ping_status, post_parente post_date_gmtche sono interessanti solo in alcune circostanze, e che in pratica vanno spesso vuota. Un altro approccio è un tavolo molto normalewp_options , molto simile, proprio come il tuoPropertyclasse. Sebbene richieda una gestione più esplicita, gli elementi in essa contenuti sono raramente vuoti. I database orientati agli oggetti e ai documenti (ad es. MongoDB) spesso hanno un tempo più facile a gestire le opzioni di modifica, perché possono creare e impostare gli attributi praticamente a piacimento.


0

Mi piace la domanda, i miei due centesimi:

I tuoi due approcci sono radicalmente diversi:

  • Il primo è OO e fortemente tipizzato, ma non estensibile
  • Il secondo è digitato debolmente (la stringa incapsula qualsiasi cosa)

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

Disgressione: si noti che alcune lingue, come C #, consentono la creazione dinamica di tipi. Quale potrebbe essere una buona soluzione per il problema generale di aggiungere membri in modo dinamico. Tuttavia, "modificare / aggiungere" tipi dopo la compilazione corrompe il sistema di tipi stesso e rende quasi inutili i tipi "modificati" (ad esempio, come accederesti a tali proprietà aggiunte, dal momento che non sai nemmeno che esistono? L'unico modo ragionevole sarebbe essere una riflessione sistematica su ogni oggetto ... che termina con un linguaggio dinamico puro _ è possibile fare riferimento alla parola chiave .NET "dinamica")


Creare tipi in fase di runtime è interessante ma troppo esotico per me (sto programmando in Java). Tale soluzione non funzionerebbe, se volessi conservare un oggetto in un database, che è sempre fortemente tipizzato, credo. La soluzione debolmente tipizzata che ho proposto può essere facilmente archiviata nel database.
Filip,

0

La creazione di un tipo in fase di esecuzione sembra molto più complicata della semplice creazione di un livello di astrazione. È molto comune creare un'astrazione per disaccoppiare i sistemi.

Lascia che ti mostri un esempio della mia pratica. Lo 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 in seguito all'introduzione di un nuovo prodotto finanziario o alla modifica dei piani di compensazione. La natura dei cambiamenti futuri 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 tutto l'inferno finanziario dietro un'astrazione. Hanno usato l'astrazione ben nota delle tabelle SQL. La maggior parte del loro core può funzionare con qualsiasi schema valido, così come il software del trader può analizzare dinamicamente lo schema e scoprire se è compatibile con il loro sistema.

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

  • Più codice per l'analisi e la convalida (e più tempo libero dal corso). Tutto ciò che il compilatore ha fatto per te con la tipizzazione statica devi fare in fase di esecuzione.
  • Più documentazione di messaggi, tabelle o altri primitivi. Tutta la complessità va dal codice a una sorta di schema o standard. Ecco un esempio del suddetto schema dell'inferno finanziario: http://ftp.moex.com/pub/FORTS/Plaza2/p2gate_en.pdf (dozzine di pagine di tabelle)

0

Questo approccio è un modello di design noto?

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

Vuoi risolvere il problema in modo diverso?

Ecco come risolverei il problema, sì.

So che ci sono lingue in cui posso aggiungere un campo in fase di esecuzione, ma per quanto riguarda il database?

Un database sarebbe come Java, in alcuni sensi. In pesudo-sql:

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

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

Ciò corrisponderebbe a Java

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

Si noti che non è necessaria una classe Proprietà, poiché la Mappa memorizza il nome come chiave.

Preferiresti aggiungere / modificare colonne o utilizzare qualcosa come mostrato sopra?

Ho provato a aggiungere / modificare la colonna ed è stato un incubo. Si può fare, ma le cose continuavano a non essere sincronizzate e non riuscivo mai a farlo funzionare bene. La struttura della tabella che ho delineato sopra ha avuto molto più successo. Se avete bisogno di fare ricerche e l'ordine da parte di, considerare l'utilizzo di un attributi tabella per ogni tipo di dati ( date_attributes, currency_attributes, ecc) o l'aggiunta di alcune delle proprietà come buoni colonne di database vecchi nella tabella prodotti. I report sono spesso molto più facili da scrivere sulle colonne del database e sulle tabelle secondarie.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.