Programming Languages Hacks

Importanti regole per linguaggi di programmazione rilevanti come Java, C, C++, C#…

  • Subscribe

  • Lettori

    I miei lettori abituali

  • Twitter

Archive for August, 2008

Sincronizzare Oggetti e Controlli Web/Windows In Un Colpo Solo

Posted by Ricibald on 14th August 2008

L’utilizzo dei controlli Web/Windows prevede due fasi:

  • il trasferimento dei dati dall’oggetto ai controlli corrispondenti
  • il trasferimento dei dati dai controlli all’oggetto corrispondente

Come dire:

void loadPage(TextBox txt_nome, Persona persona) {
    txt_nome.Text = persona.Nome;
}

void submitInfo(TextBox txt_nome, Persona persona) {
    persona.Nome = txt_nome.Text;
}

Questo è il classico approccio, ma, come tutte le cose meccaniche che si fanno in informatica, nasconde vari problemi:

  1. il tempo: scrivo le stesse cose due volte. Prima ad andare e poi a tornare…
  2. la consistenza: qualche proprietà potrei dimenticarla e l’oggetto potrebbe non essere aggiornato o potrebbe essere popolato con il valore sbagliato

La cosa ideale sarebbe avere un controllo Web/Windows che consentisse di realizzare il bind a due vie (le due fasi le fa il controllo per noi):

void bind(TextBoxBinded txt_nome, Persona persona) {
    txt_nome.Bind(persona.Nome);
}

Questa “sarebbe” la cosa ideale, ma non è praticabile: come conosciamo il set della proprietà “Nome”? In questo modo non possiamo:

Come passare per parametro i metodi get/set di una proprietà o di un campo, in modo da ottenere il bind a due vie?

Dopo ricerche su internet, miei ragionamenti e varie ed eventuali, le soluzioni possibili sono le seguenti:

  1. Approccio basato su Reflection: la soluzione più immediata, ma molto fragile: per ottenere un oggetto Property dobbiamo passare il nome della proprietà. Tale nome è una semplice stringa: non è pertanto tipizzato in alcun modo ed eventuali modifiche alla proprietà non ci verrebbero segnalate dal compilatore… Con questo approccio ogni modifica alla struttura ad oggetti diventerebbe letteralmente un incubo…
  2. Ogni proprietà implementa un interfaccia specifica: ad esempio IProperty<T>, dove T è il tipo della proprietà. Tale interfaccia avrà metodi get e set che il controllo Web/Windows riconosce. Il metodo bind accetterà quindi solo oggetti di tipo IProperty. L’approccio ha un ulteriore vantaggio: l’interfaccia può essere pensata come wrapper per contenere metainformazioni. Non solo quindi get o set, ma anche informazioni come editabilità, visibilità, nome, …
    Per abilitarla si devono modificare tutte le proprietà in modo che il tipo non sia ad es. String, ma IProperty<String>: questo però provoca una modifica nell’interfaccia della proprietà, e tutti i metodi che prima utilizzavano ad es. persona.Nome.ToUpper non funzioneranno più.
    Una soluzione eccellente stà nel realizzare degli operatori di conversione impliciti da IProperty<String> a String e nell’implementare le stesse identiche funzioni di String in un suo proxy: PropertyString, che implementa IProperty<String> e dichiara le stesse funzioni di String, rigirando tali funzioni al valore String che contiene.
    Con questo approccio si possono trattare a tutti gli effetti gli IProperty<T> come se fossero effettivamente dei T: i client ne possono essere inconsapevoli…
    Si hanno però svantaggi seguenti:

    • si deve aver accesso alla modifica delle proprietà delle classi. Se della struttura ad oggetti non abbiamo il controllo l’unica soluzione che ci rimane per continuare ad usare il nostro approccio è realizzare un Adapter. Una soluzione piuttosto noiosa…
    • si devono implementare tutte le classi che emulano i tipi primitivi: PropertyString, PropertyInt,  PropertyBool, … è anche vero che una volta fatto diventa patrimonio vostro e rimarrà lì con voi…
  3. Approccio basato su Reflection Tipizzata Fortemente: una splendida soluzione basata su LambdaExpression e Generics proposta da Nick Butler. Il segreto è utilizzare i Generics per dichiarare tramite LambdaExpression la proprietà da “bindare”, più o meno in modo simile alla OrderBy di LINQ:
    PropertyInfo prop = typeof(Persona).GetProperty("Nome"); // VECCHIO APPROCCIO: NON TIPIZZATO
    PropertyInfo prop = Instance<Persona>.GetProperty( o => o.Nome);

    Dove avremo:

    public static class Instance<TClass>
    {
        public static PropertyInfo GetProperty<T>(
                              Expression<Func<TClass, T>> m ) 
        {
            LambdaExpression lambda = m;
            Expression bodyExpr = lambda.Body;
            MemberExpression memberExpr = (MemberExpression)bodyExpr;
            MemberInfo memberInfo = memberExpr.Member;
            PropertyInfo propInfo = (PropertyInfo)memberInfo;
            return propInfo;
        }
    }

    Nonostante l’eleganza dell’approccio (specie se combinato con extension method (si veda l’implementazione di TextBoxFor nell’articolo di MSDN), questo ha un punto in svantaggio rispetto al precedente: consente di inserire solo metadati statici tramite attributi. Viceversa, il vantaggio rispetto alla precedente è che non è necessario modificare nulla del codice preesistente e non bisogna implementare i proxy per ogni tipo primitivo

Insomma, ora che sapete tutto manca “solo” di scrivere le vostre versioni custom dei controlli che consentono il bind (consiglio un Decorator…)

Posted in .net | 1 Comment »

Metafora Aritmetica per i Design Pattern

Posted by Ricibald on 13th August 2008

Dal blog di Adrian Florea (e quindi dal libro di Uwe Aßmann) mi ha colpito molto questa metafora aritmetica per esprimere il concetto dei design pattern:

The basic solution strategy of a design pattern is factoring:

a * b + a * d = a * (b + d)

Design patterns are the “binomial formulas” of software engineering!

Infatti quello che fanno i design pattern è fattorizzare, mettere in comune aspetti altrimenti ripetuti, come:

  • porzioni di codice in un metodo: Pattern Template
  • espressioni, salti condizionati: Pattern Strategy, State, Template, Factory, … in generale l’utilizzo sensato del polimorfismo (cioè il senso dei pattern)
  • metodi interi: spesso sintomo di cattiva assegnazione di responsabilità. I pattern consentono di individuare le responsabilità in modo da condividere gli stessi metodi nell’ambito della stessa classe padre

Un ottimo articolo che ne parla è quello di UGIdotNET.

Posted in .net | 5 Comments »

Applicazioni Distribuite: Proprietà Statiche vs Caching. Singleton rivisitato

Posted by Ricibald on 13th August 2008

Normalmente quando scriviamo codice capita di scrivere proprietà statiche: si pensi al pattern Singleton. Lo scopo di questo pattern è proprio assicurare la condivisione degli elementi, per ragioni di efficienza o di consistenza.

Il pattern funziona bene in contesti “normali”: è implementato mediante un campo/proprietà statico e quindi condiviso nell’ambito dell’applicazione Console/Windows che sviluppiamo. Se però ci spostiamo in contesti distribuiti il pattern non è in realtà così efficace:

In contesti distribuiti, i campi statici sono condivisi solo nell’ambito della stessa richiesta

Quindi in una nuova richiesta viene “dimenticato” il valore assunto degli stessi campi statici in altre richieste. I campi statici nascono invece con lo scopo di avere memoria globale di un’informazione. Quindi, come regola generale:

Il concetto di “campi static” dovrebbe sempre essere virtualizzato

Questo significa che, come possibile soluzione, la proprietà statica deve essere acceduta mediante una classe astratta CacheManager, che:

  1. è ottenibile come Singleton “puro”, cioè come proprietà statica a cui siamo tanto abituati. Avremo quindi un metodo CacheManager.GetInstance che restituisce tale istanza, condivisa solo nell’ambito della richiesta corrente
  2. consente metodi come SetPrinterSingleton o GetPrinterSingeton che restituiscono tali valori in cache
  3. l’implementazione di tale metodi varia a seconda del tipo concreto in uso

Il terzo punto significa che avremo diverse implementazioni, come StaticCacheManager o WebApplicationCacheManager, in cui la prima prenderà valori nel modo classico, tramite proprietà di istanza, mentre la seconda implementazione li prenderà dalle variabili “Application” del nostro web site.

La scelta di quale implementazione concreta utilizzare è demandata al momento in cui viene creato internamente l’istanza unica di CacheManager, che ci restituirà l’istanza corretta sulla base del principio “Dependency Injection: viene specificato cosa creare mediante file di configurazione e/o Reflection.

Molto comodo risulta infine utilizzare un Singleton con “shortcut”: invece di scrivere

public abstract class CacheManager {
    public static CacheManager GetInstance() {
        return ServiceLocator.GetCacheManager(); // usa reflection
    }
    public abstract Printer GetPrinterSingleton();
}

scriveremo una forma molto più compatta da utilizzare per il “client” della classe:

public abstract class CacheManager {
    public static CacheManager GetInstance() {
        return ServiceLocator.GetCacheManager(); // usa reflection
    }
    public static Printer GetPrinterSingeton() {
        return GetInstance().GetPrinterSingleton();
    }
    protected abstract Printer GetPrinterSingleton();
}

Che verrà quindi utilizzato in questo modo:

public sealed class Printer {
    private Printer() {
    }
    public static Printer GetInstance() {
        // lock regione critica omessi per semplicita'
        Printer printer = CacheManager.GetPrinterSingleton();
        if(printer == null) {
            // il set restituisce la stessa istanza passata per parametro
            printer = CacheManager.SetPrinterSingleton(new Printer());
        }
        return printer;
    }
}

Si può anche esprimere in una forma succinta, utilizzando l’operatore di null coalescing di C#:

public sealed class Printer {
    private Printer() {
    }
    public static Printer GetInstance() {
        return CacheManager.GetPrinterSingleton() ?? CacheManager.SetPrinterSingleton(new Printer());
    }
}

Si può infine completare l’implementazione garantendo la mutua esclusione utilizzando l’approccio di William Pugh:

public sealed class Printer {
    private Printer() {
    }
    public static Printer GetInstance() {
        return SingletonHolder.GetInstance();
    }

    class SingletonHolder {
        static SingletonHolder() { }
        static Printer GetInstance() {
            return CacheManager.GetPrinterSingleton() ?? CacheManager.SetPrinterSingleton(new Printer());
        }
    }
}

Attenzione: si potrebbe pensare che lo stesso concetto si applichi anche al pattern Flyweight. In realtà questo pattern (a differenza dell’esempio riportato nell’articolo in Wikipedia) non dovrebbe prevedere una mappa statica, ma a livello istanza, quindi la regola descritta è comunque soddisfatta.

In pratica la regola da seguire è questa:

Campi/proprietà statiche hanno senso solo nel contesto del pattern Singleton, e in tal caso il concetto di “campi static” deve essere virtualizzato tramite un CacheManager, a sua volta creabile tramite la versione “standard” del pattern Singleton

Posted in .net | 12 Comments »