E se i globi avessero un senso?


10

Ho un valore di cui molti oggetti hanno bisogno. Ad esempio, un'applicazione finanziaria con diversi investimenti come oggetti e la maggior parte di essi necessita dell'attuale tasso di interesse.

Speravo di incapsulare il mio "ambiente finanziario" come oggetto, con il tasso di interesse come proprietà. Ma gli oggetti fratelli che hanno bisogno di quel valore non possono raggiungerlo.

Quindi, come posso condividere valori tra molti oggetti senza accoppiare in modo eccessivo il mio design? Ovviamente sto pensando a questo sbagliato.


2
Il tasso di interesse è fissato per la durata dei tuoi calcoli o stai facendo qualcosa di simile a una simulazione in cui può variare tra i timestep?
James,

È più simile a una simulazione: può cambiare durante la corsa.
Greg

In tal caso, ogni investimento ha davvero bisogno di salvare il tasso di interesse o può riceverlo tramite un parametro in una updatefunzione chiamata ad ogni timestep? Puoi pubblicare in pseudocodice come funziona la tua simulazione?
James,

3
Sei la strada giusta, a Singletonè un globale con zucchero sintetico OO su di esso ed è una soluzione terribile che accoppia strettamente il tuo codice in alcuni dei modi peggiori possibili. Leggi questo articolo più volte fino a quando non lo capisci!

Il tasso di interesse è come una serie temporale che è una funzione che accetta DateTimecome input e restituisce un numero come output.
rwong

Risposte:


14

Ho un valore di cui molti oggetti hanno bisogno.

Questo è un odore di design. Non è comune che molti oggetti debbano sapere qualcosa. Detto questo, l'attuale tasso di interesse è un buon esempio di circostanze eccezionali. Una cosa di cui preoccuparsi è che raramente c'è il tasso di interesse. Diversi strumenti finanziari utilizzano tassi diversi. Per lo meno, locali diversi utilizzano tariffe "standard" diverse. Inoltre, per facilitare i test e i rapporti, di solito vorrai passare una tariffa poiché non desideri utilizzare la tariffa corrente lì. Desideri utilizzare la tariffa "what if" o "a partire dalla data del rapporto".

Quindi, come posso condividere valori tra molti oggetti senza accoppiare in modo eccessivo il mio design?

Condividendoli, non facendoli tutti fare riferimento a una singola istanza. Passare la stessa cosa è ancora accoppiarsi in una certa misura, ma non in eccesso poiché è necessario qualcosa come l'attuale tasso di interesse come input per una varietà di calcoli.


17
Il problema con circostanze eccezionali è che nel mondo reale i software non sono così eccezionali.
Mattnz,

Penso che questo sia un grave fraintendimento. I nostri algoritmi esistono contemporaneamente in diversi contesti. Il primo è il contesto globale. Quindi "questo" contesto per linguaggi orientati agli oggetti. Contesto della sessione in un servizio Web, contesto della transazione in un ambiente DB, Finestra principale per una GUI, ... e ci sono informazioni che appartengono a questi contesti. Devono "stare in giro" ed essere disponibili, lo stesso set (oggetti) per chiunque si trovi nello stesso contesto. Questo va bene. Il problema è risolverlo per ogni oggetto, non creando un servizio di contesto o usando un framework, come Spring in Java.
Lorand Kedves,

3
Non c'è nulla di eccezionale nell'attuale tasso di interesse. Ci sono molti esempi di articoli del mondo reale che hanno un valore: - Limite di velocità su strada aperta, livello accettabile di alcol nel sangue, aliquota fiscale GST (o IVA) per nominarne solo alcuni. Questa è la differenza tra scienza e ingegneria: gli ingegneri risolvono i problemi del mondo reale di oggi, gli scienziati sognano il giorno in cui il mondo reale si inserirà in belle scatole di perfezione e risolverà quei problemi.
Mattnz,

1
Ho scelto questa come risposta perché è semplice e non si basa su un decennio di esperienza OOP per grok. MOLTE grazie a tutti gli intervistati. Ho trascorso un'intera giornata di lettura grazie ai numerosi riferimenti, ma rimango ancora un po 'perplesso. Per una domanda così semplice, sono rimasto sorpreso dalla varietà e dall'emozione dietro le risposte. Rimango convinto che a volte esiste una fonte centrale di dati globali ma variabili, che è meglio servita da un Singleton. Non credo che si dovrebbero passare i puntatori su e giù in una gerarchia di oggetti solo per evitare un Singleton. Grazie ancora a tutti.
Greg,

@mattnz, il problema è che ognuno dei tuoi esempi è variabile nei casi in cui distribuisci il tuo programma a più basi utenti che possono estendersi ad aziende, stati o paesi. Tutti possono anche essere variabili nel tempo.
Dan Lyons,

10

In questo caso specifico, utilizzerei il modello Singleton . L'ambiente finanziario sarebbe l'oggetto di cui sono a conoscenza tutte le altre librerie di classi, ma sarebbe istanziato da Singleton. Idealmente, invieresti quell'oggetto istanziato alle varie librerie di classi.

Per esempio:

  • Service Layer (libreria di classi): crea un'istanza dell'oggetto FinancialEnvironment tramite un singleton
  • Livello logico aziendale (libreria di classi): accetta l'oggetto FinancialEnvironment dal livello di servizio
  • Livello di accesso ai dati (libreria di classi): accetta l'oggetto FinancialEnvironment dal livello di servizio (o, a seconda dell'architettura, il livello di logica aziendale). O forse Singleton invoca il livello di accesso ai dati per ottenere informazioni, come il tasso di interesse, da un repository (database / servizio web / servizio WCF).
  • Libreria di classi di entità (o DTO, se si desidera chiamarlo così): dove vive l'oggetto FinancialEnvironment. Tutte le altre librerie di classi hanno un riferimento alla libreria di classi Entità.

Le altre classi sono collegate solo tramite la libreria di classi Entità, accettano un oggetto FinancialEnvironment istanziato. A loro non importa come è stato creato, fa solo il livello di servizio, tutto ciò che vogliono è l'informazione. Il singleton potrebbe anche essere abbastanza intelligente da archiviare diversi oggetti FinancialEnvironment, a seconda delle regole per il locale come sottolineato da @Telastyn.

Da un lato, non sono un grande fan del modello Singleton, lo considero un odore di codice, in quanto può essere utilizzato in modo molto semplice. Ma in alcuni casi ne hai bisogno.

Aggiornare:

Se devi assolutamente, positivamente avere una variabile globale, l'implementazione del Singleton Pattern come descritto sopra funzionerebbe. Tuttavia, non sono un grande fan di questo, e sulla base dei commenti del mio post originale, molte altre persone non lo sono neanche. Qualcosa di volatile come un InterestRate, un Singleton potrebbe non essere la soluzione migliore. I singoli funzionano meglio quando le informazioni non cambiano. Ad esempio, ho usato un Singleton in una delle mie applicazioni per istanziare i contatori delle prestazioni. Perché se cambiano, allora devi avere la logica in atto per gestire i dati da aggiornare.

Se fossi un uomo di scommesse, scommetterei che il tasso di interesse è stato memorizzato da qualche parte in un database, o è stato recuperato tramite un servizio web. In tal caso, si consiglia un repository (livello di accesso ai dati) per recuperare tali informazioni. Per evitare viaggi inutili nel database (non sono sicuro della frequenza con cui i tassi di interesse cambiano o altre informazioni nella classe FinancialInformation), è possibile utilizzare la memorizzazione nella cache. Nel mondo C # la libreria Caching Application Block di Microsoft funziona molto bene.

L'unica cosa che cambierebbe dall'esempio sopra, sarebbero le varie classi nel livello di servizio che necessitavano di FinancialInformation che avrebbero recuperato dal livello di accesso ai dati invece che da Singleton che creava un'istanza dell'oggetto.


8
Ugh. Rendere il globale un singleton non lo rende meno maleodorante. Semmai ti sei limitato molto di più.
Telastyn,

3
@DavidCowden non importa se non sono complessi da implementare; sfuggono al tuo design peggio di quanto non facciano i globi. Sono globali e applicano restrizioni (non necessarie) che ne hai solo una.
Telastyn,

4
Stavo per pubblicare un commento sarcastico dicendo "rendilo un singleton e passerà dalla cattiva pratica alla migliore pratica", ma poi ho visto che era già una risposta accettata e votata. Molto bella!
Kevin,

4
Singletonè un globale con zucchero sintetico OO e una stampella per i più pigri e deboli di mente. Singleton/globalè il modo peggiore in assoluto per associare strettamente il tuo codice a qualcosa che sarà un cancro in seguito quando ti rendi conto di quale idea colossalmente cattiva è stata e perché tutti dicono che lo sono!

4
@Telastyn: è una sfortunata realtà che i progetti più perfetti una volta che escono dal mondo perfettamente ordinato del design del software teorico e si uniscano al mondo reale, vengano fubar'd.
Mattnz,

4

File di configurazione?

Se hai valori usati "a livello globale", inseriscili in un file di configurazione. Quindi ogni sistema e sottosistema può fare riferimento a questo e estrarre le chiavi necessarie, renderle di sola lettura.


Quindi l'utente dovrebbe aggiornare un file di configurazione ogni volta che il tasso di interesse cambia?
Caleb,

2
Perchè no? dipende dalla "variabile", ovviamente le cose che cambiano frequentemente dovrebbero essere collocate all'interno di un archivio dati CENTRALIZZATO.
Darknight,

1

Sto parlando dell'esperienza di chi ha circa un mese di manutenzione su un progetto di buone dimensioni (circa 50k LOC) che abbiamo appena pubblicato.

Posso dirti che probabilmente non vuoi davvero un oggetto globale. L'introduzione di questo tipo di cruft offre molte più opportunità di abuso di quanto non aiuti.

Il mio suggerimento iniziale è che se hai diverse classi che necessitano di un tasso di interesse corrente, probabilmente vorrai semplicemente farle implementare una IInterestRateConsumero qualcosa del genere. All'interno di quell'interfaccia avrai un SetCurrentInterestRate(double rate)(o qualunque cosa abbia senso), o forse solo una proprietà.

Il passaggio di un tasso di interesse in realtà non è accoppiamento: se la tua classe ha bisogno di un tasso di interesse, fa parte della sua API. È solo accoppiamento se una delle tue classi inizia a preoccuparsi esattamente di come l'altra classe utilizza quel tasso di interesse.


Passare un tasso di interesse in giro è l' accoppiamento, semplicemente non è un cattivo accoppiamento.
Vaughandroid,

1

Martin Fowler ha un articolo che parla brevemente di come trasformare un globale statico in qualcosa di più flessibile. Fondamentalmente lo trasformi in un singleton, quindi modifichi il singleton in modo che supporti l'override della classe dell'istanza con una sottoclasse (e se necessario sposta la logica che crea l'istanza in una classe separata che può essere sottoclassata, cosa che faresti se la creazione dell'istanza di superclasse quindi la sua sostituzione in seguito è un problema).

Certo, devi soppesare i problemi con i singoli (anche i singleton sostituibili) rispetto al dolore di passare lo stesso oggetto ovunque.

Per quanto riguarda l'oggetto "ambiente finanziario", è conveniente programmare al primo passaggio, ma quando hai finito hai aggiunto alcune dipendenze extra. Le classi che necessitano solo di un tasso di interesse ora funzionano solo quando viene passato un oggetto di ambiente finanziario, il che renderà difficile il loro riutilizzo quando non si dispone di un oggetto di ambiente finanziario che giace in giro. Quindi scoraggerei passandolo ampiamente.


0

Perché non mettere i dati sui tassi di interesse in una cache centrale?

È possibile utilizzare una delle numerose librerie di cache, a seconda delle esigenze più adatte, qualcosa come memcached risolve tutti i problemi di concorrenza e gestione del codice e consentirà all'applicazione di adattarsi a più processi.

Oppure vai su tutto il maiale e archiviali in un database, che ti permetterà di scalare su più server.


0

In tali situazioni ho introdotto con successo (riutilizzato) il termine "contesto" con a volte più livelli.

Ciò significa un singleton, quindi un negozio di oggetti "globale", dal quale è possibile richiedere questo tipo di oggetti. I codici che li richiedono, includono l'intestazione del negozio e usano le funzioni globali per ottenere le loro istanze di oggetti (come ora, il fornitore di tassi di interesse).

Il negozio può essere:

  • tipizzato rigorosamente: includi le intestazioni per tutti i tipi serviti e quindi puoi creare accessi tipizzati, come InterestRate getCurrentInterestRate ();
  • o generico: Object getObject (enum obType); ed estendere l'enum obType solo con i nuovi tipi (obtypeCurrentInterestRate).

Più grande è il sistema, più utilizzabile quest'ultima soluzione, con un rischio piuttosto piccolo di usare l'enum sbagliato. D'altra parte, con le lingue che consentono dichiarazioni di tipo forward, penso che tu possa usare gli accessi digitati senza includere tutte le intestazioni nel negozio.

Un'altra nota: potresti avere più istanze dello stesso tipo di oggetto per usi diversi, come a volte valore della lingua diverso per la GUI e per i log di stampa, globali e di sessione, ecc., Quindi il nome enum / accessor NON deve riflettere il tipo effettivo , ma il ruolo dell'istanza richiesta (CurrentInterestRate).

Nell'implementazione del negozio, è necessario gestire i livelli di contesto e le raccolte di istanze di contesto. Un semplice esempio è il servizio Web, in cui si ha il contesto globale (un'istanza per tutte le richieste per quell'oggetto - problematico quando si ha una server farm) e un contesto per ogni sessione web. Puoi anche avere contesti per ogni utente, che può avere più sessioni parallele, ecc. Con più server dovresti usare una specie di cache distribuita per tali cose.

Quando arriva la richiesta, decidi quale livello di contesto è l'oggetto richiesto, ottieni quel contesto per la chiamata. Se l'oggetto è lì, lo rispedisci; in caso contrario, lo crei e lo memorizzi a quel livello di contesto e lo restituisci. Naturalmente, sincronizza la sezione di creazione (e pubblicala nella cache distribuita). La creazione può essere configurabile come plug-in, preferibilmente con linguaggi che consentono di creare istanze di oggetti in base al nome della loro classe (Java, Obiettivo C, ...), ma è possibile farlo anche in C con librerie collegabili con funzioni di fabbrica.

Nota a margine: il chiamante NON dovrebbe conoscere troppo i propri contesti e il livello di contesto dell'oggetto richiesto. Ragioni: 1: è facile commettere errori (o "trucchi intelligenti") giocando con questi parametri; 2: il livello di contesto della richiesta potrebbe cambiare in seguito. Collego principalmente informazioni di contesto al thread, quindi l'archivio oggetti ha le informazioni senza parametri aggiuntivi dalla richiesta.

D'altra parte, la richiesta può contenere un suggerimento per l'istanza: come ottenere il tasso di interesse per una data specifica. Dovrebbe essere lo stesso accesso "globale", ma più istanze a seconda della data (e portando valori di data diversi alla stessa istanza tra le variazioni di tasso), quindi è consigliabile aggiungere un oggetto "suggerimento" alla richiesta, utilizzato da fabbrica dell'istanza e non il negozio; e un keyForHint per la fabbrica, utilizzato dal negozio. È possibile aggiungere queste funzioni in seguito, ho appena accennato.

Nel tuo caso si tratta di una sorta di overkill (solo un oggetto viene offerto a livello globale), ma per un codice aggiuntivo piuttosto piccolo e semplice in questo momento, ottieni un meccanismo per ulteriori, forse requisiti più complessi.

Un'altra buona notizia: se sei in Java, ottieni questo servizio da Spring senza pensarci troppo, volevo solo spiegarlo in dettaglio.


0

Il motivo per NON utilizzare un globale (o singleton) è che anche se inizialmente ti aspetti di avere un solo valore, è spesso sorprendentemente utile poter utilizzare lo stesso codice più volte nello stesso programma, ad esempio:

  • per calcolare cosa accadrebbe se il tasso di interesse fosse diverso
  • avere alcuni componenti dipende dal tasso di interesse degli Stati Uniti e alcuni componenti dipendono dal tasso di interesse del Regno Unito

Vorrei rendere il tasso di interesse un membro della classe "strumento finanziario", e accetterei che si debba passare a qualsiasi classe di membri (sia per calcolo, sia dando loro un puntatore / gancio su di esso durante la costruzione).


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.