E se i globals hanno senso?

10

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

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

Quindi, come posso condividere i valori tra molti oggetti senza sovrascrivere il mio design? Ovviamente sto pensando a questo sbagliato.

    
posta Greg 10.07.2012 - 01:40
fonte

9 risposte

14

I've got a value that many objects need.

Questo è un odore di design. È raro che molti oggetti abbiano bisogno di sapere qualcosa. Detto questo, il tasso di interesse attuale è un esempio abbastanza buono di circostanze eccezionali. Una cosa di cui preoccuparsi è che raramente il tasso di interesse. Diversi strumenti finanziari utilizzano tassi diversi. Per lo meno, diverse localizzazioni usano diverse tariffe "standard". Inoltre, per facilitare i test e i rapporti, di solito vuoi passare una percentuale dal momento che non vuoi utilizzare la tariffa attuale . Desideri utilizzare la tariffa "what if" o "a partire dalla data di segnalazione".

So how do I share values among many objects without over-coupling my design?

Condividendoli, non averli tutti si riferiscono a una singola istanza. Passare la stessa cosa è ancora un accoppiamento, ma non un oltre poiché è necessario qualcosa come il tasso di interesse corrente come input per una varietà di calcoli.

    
risposta data 10.07.2012 - 01:48
fonte
10

In questa particolare istanza utilizzerei il Singleton Pattern . Il FinancialEnvironment sarebbe l'oggetto di cui tutte le altre librerie di classi sono a conoscenza, ma sarebbe istanziato dal Singleton. Idealmente, invierai quell'oggetto istanziato alle varie librerie di classi.

Ad esempio:

  • Service Layer (libreria di classi) - Istanzia l'oggetto FinancialEnvironment tramite un singleton
  • Business Logic Layer (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 della logica di business). O forse Singleton invoca il Data Access Layer per ottenere informazioni, come il tasso di interesse, da un repository (servizio database / servizio web / WCF).
  • Entità (o DTO se si desidera chiamarla così) libreria di classi - Dove vive l'oggetto FinancialEnvironment. Tutte le altre librerie di classi hanno un riferimento alla libreria di classi Entities.

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

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

Aggiornamento:

Se si deve assolutamente avere una variabile globale, implementare il Pattern Singleton come descritto sopra funzionerebbe. Tuttavia, io non sono un grande fan di questo, e in base ai commenti del mio post originale, molte altre persone non lo sono neanche. Come qualcosa di volatile come InterestRate, un Singleton potrebbe non essere la soluzione migliore. I single funzionano meglio quando le informazioni non cambiano. Ad esempio, ho utilizzato un Singleton in una delle mie applicazioni per creare un'istanza dei contatori delle prestazioni. Perché se cambiano, è necessario disporre di una logica per gestire i dati aggiornati.

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

L'unica cosa che cambierebbe dall'esempio precedente, sarebbero le varie classi nel livello di servizio che necessitano che FinancialInformation recuperi dal livello di accesso ai dati anziché dal Singleton che crea un'istanza dell'oggetto.

    
risposta data 10.07.2012 - 02:12
fonte
4

File di configurazione?

Se disponi di valori che vengono utilizzati "globalmente", inseriscili in un file di configurazione. Quindi ogni sistema e sottosistema può fare riferimento a questo e tirare le chiavi necessarie, renderle di sola lettura.

    
risposta data 10.07.2012 - 10:18
fonte
1

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

Posso dirti che probabilmente non vuoi veramente un oggetto globale. Introdurre quel tipo di cruft offre molte più opportunità di abuso che aiuto.

Il mio suggerimento iniziale è che se hai diverse classi che hanno bisogno di un tasso di interesse attuale, probabilmente vorrai semplicemente implementarle un IInterestRateConsumer o qualcosa del genere. All'interno di questa interfaccia avrai una SetCurrentInterestRate(double rate) (o qualsiasi altra cosa abbia senso), o forse solo una proprietà.

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

    
risposta data 10.07.2012 - 03:01
fonte
1

Martin Fowler ha un articolo che parla brevemente di come rifattorizzare un globale statico in qualcosa di più flessibile. Fondamentalmente lo si trasforma in un singleton, quindi si modifica il singleton in modo che supporti l'override della classe dell'istanza con una sottoclasse (e se necessario si sposta la logica che crea l'istanza in una classe separata che può essere sottoclassificata, cosa che si farebbe se si crea l'istanza di super-classe, sostituirla successivamente è un problema).

Ovviamente, devi soppesare i problemi con i singleton (anche i singleton sostituibili) e il dolore di passare lo stesso oggetto ovunque.

Per quanto riguarda l'oggetto "ambiente finanziario" - è conveniente programmare sul primo passaggio, ma quando hai finito hai aggiunto alcune dipendenze extra. Le classi che hanno solo bisogno di un tasso di interesse ora funzionano solo quando superano un oggetto dell'ambiente finanziario, il che le rende difficili da riutilizzare quando non si ha un oggetto finanziario che gira intorno. Quindi scoraggeremmo di trasmetterlo ampiamente.

    
risposta data 10.07.2012 - 20:01
fonte
0

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

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

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

    
risposta data 10.07.2012 - 05:51
fonte
0

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

Questo significa un singleton, quindi un "object" globale, da cui è 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 oggetto (come ora, il fornitore di tasso di interesse).

Il negozio può essere:

  • tipizzato rigorosamente: includi le intestazioni per tutti i tipi serviti e quindi puoi creare accessors digitati, come InterestRate getCurrentInterestRate ();
  • o generico: oggetto getObject (enum obType); e solo estendere obType enum con i nuovi tipi (obtypeCurrentInterestRate).

Più grande è il sistema, più è utilizzabile quest'ultima soluzione, per un rischio piuttosto ridotto di usare l'enum sbagliato. D'altra parte, con le lingue che consentono dichiarazioni di tipo forward, penso che sia possibile utilizzare accessors digitati senza includere tutte le intestazioni nel negozio.

Un'altra nota: potresti avere più istanze dello stesso tipo di oggetto per usi diversi, ad esempio il valore della lingua a volte diverso per la GUI e per la stampa, i registri 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, devi gestire i livelli di contesto e le raccolte di istanze del 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 un tipo 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 rimandi; in caso contrario, lo crei e lo memorizzi a quel livello di contesto e lo restituisci. Ovviamente, sincronizzare la sezione di creazione (e pubblicarla nella cache distribuita). La creazione può essere configurabile come plug-in, meglio con i linguaggi permettendo la creazione di istanze di oggetti con il loro nome di classe (Java, Objective C, ...), ma è possibile farlo anche in C con le librerie collegabili con funzioni di fabbrica.

Nota a margine: il chiamante NON deve conoscere troppo i propri contesti e il livello di contesto dell'oggetto richiesto. Motivi: 1: è facile fare errori (o "trucchi intelligenti") giocando con questi parametri; 2: il livello di contesto richiesto potrebbe cambiare in seguito. Per lo più collego le informazioni di contesto al thread, quindi l'archivio oggetti ha le informazioni senza parametri aggiuntivi dalla richiesta.

D'altra parte, la richiesta potrebbe contenere suggerimenti 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 diversi valori di data alla stessa istanza tra le variazioni di velocità), quindi è consigliabile aggiungere un oggetto "suggerimento" alla richiesta, utilizzato dal istanza di fabbrica e non il negozio; e un keyForHint alla fabbrica, usato dal negozio. Puoi aggiungere queste funzioni in seguito, ho appena citato.

Per il tuo caso questo è un tipo di overkill (solo un oggetto è servito a livello globale), ma per un codice extra abbastanza piccolo e semplice in questo momento, ottieni un meccanismo per ulteriori, forse più complessi requisiti.

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

    
risposta data 10.07.2012 - 10:12
fonte
0

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

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

Renderei il tasso di interesse un membro della classe "strumento finanziario" e accetto che devi passarlo a qualsiasi classe membro (o per calcolo, o dandogli un puntatore / gancio per la costruzione) .

    
risposta data 11.07.2012 - 11:42
fonte
0

Le cose dovrebbero essere passate nei messaggi, non letti da una cosa globale.

    
risposta data 15.08.2012 - 17:24
fonte

Leggi altre domande sui tag