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à.