Programming
In questa seconda parte (qui la prima) completerò la parte applicativa introducendo lo strato
di factory e cercherò di fare un'analisi, spero oggettiva, del laro svolto dei
sui limiti e presenterò alcune soluzioni a questi limiti.
Factory LayerNella mia ottica il factory si
occupa solo ed esclusivamente della generazione delle istanze delle classi di
business, il factory conosce molto bene lo strato di business. Partiamo, come
sempre, dal codice che non differisce molto da quello presentato nel precedente articolo riguardo allo strato di persistenza:
//// ASSEMBLY// Topics.Common.Factory.dll//public sealed class FactoryEngine{ private FactoryEngine() { } private static FactoryEngine _engine; /* Singleton */ public static FactoryEngine Engine { get { if( _engine == null ) { _engine = new FactoryEngine(); } return _engine; } } private MyCustomerFactory _myCustomers public MyCustomerFactory MyCustomers { get { if( this._myCustomers == null ) { this._myCustomers = MyCustomerFactory.CreateInstance(); } return this._myCustomers; } }}public abstract class MyCustomerFactory{ public static MyCustomerFactory CreateInstance() { /* OMISSIS Legge da file .config quale tipo di Factory debba istanziare e da quale assembly, tramite Activator.CreateInstance( ... ) inizializza la classe "reale" corretta */ } /* MyCustomerCollection (omissis) è una semplice collection fortemente tipizzata di MyCustomers */ public abstract MyCustomerCollection FindCustomersByCompanyName( string companyName ); public abstract MyCustomer FindByGuid( Guid pk );}//// ASSEMBLY// Topics.MyApplication.SqlFactory.dll//public class MyCustomerSqlFactory : MyCustomerFactory{ public override MyCustomerCollection FindCustomersByCompanyName( string companyName ) { //Si connette a SQL //Invoca la SP del caso //Ottiene un SqlDataReader (IDataReader) //Crea la collection passando al costruttore il DataReader } public override MyCustomer FindByGuid( Guid pk ) { //Si connette a SQL //Invoca la SP del caso //Ottiene un SqlDataReader (IDataReader) //Crea l'istanza della classe passando al costruttore il DataReader }}
Anche qui vale la stessa regola esposta per lo strato di persistenza,
possiamo sostituire il tipo factory a caldo...
Approfitto di questi schermi per...
Ogni tanto (IMHO non abbastanza) sui NewsGroup si
parla di design e l'argomento principe per chi si sta approcciando al problema è
quasi sempre la stratificazione (suddivisione in Layer) delle
applicazioni.Naturalmente il "problema" l'ho dovuto
sviscerare anche io e dopo varie reimplementazioni l'ho "rilsolto" (almeno per
ora).
Quando sono di fronte ad applicazioni di un certo "spessore" la struttura è sintetizzabile
così:
Domain LayerRappresenta lo strato di business e contiene
tutte le classi/entities necessarie.Una esempio tipo di business entity(s)
potrebbe essere:
//// ASSEMBLY// Topics.Common.dll///* MyObject è la classe di base da cui tutte le entity del mio framework erditano*/public abstract class MyObject{ protected virtual void Initialize( System.Data.IDataReader reader ) { if( reader == null ) { /* L'istanza rappresenta un nuovo oggetto quindi inizializziamo i field con i valori di default */ this._uid = Guid.NewGuid(); this._description = String.Empty; this._isNew = true; } else { this._uid = reader.GetGuid( reader.GetOrdinal( "UID" ) ); this._description = reader.GetString( reader.GetOrdinal( "Description" ) ); this._isNew = false; } } protected MyObject( System.Data.IDataReader reader ) { if( reader == null ) { throw new ArgumentException( "Invalid Source" ); } this.Initialize( reader ); } protected MyObject() { this.Initialize( null ); } private Guid _uid; //La chiave primaria public Guid UID { get{ return this._uid; } } private bool _isNew; /* La proprietà in sola lettura IsNew ci permette di sapere se l'istanza che stiamo maneggiando arriva da un DataSource o sia una nuovo elemento */ public bool IsNew { get{ return this._isNew; } } private string _description; public string Description { get{ return this._description; } set { if( value == null ) { value = String.Empty; } if( !value.Equals( this._description ) ) { this._description = value; } } } /* ParametersCollection (omessa per semplicità) non è che una collection di coppia Key (String)/Value (Object) che viene usata a mo di MOC object al fine di scambiare dati con lo strato di factory */ protected virtual ParametersCollection GetParameters() { ParametersCollection parameters = new ParametersCollection(); parameters.Add( "UID", this.UID ); parameters.Add( "Description", this.Description ); return parameters; } protected abstract void InsertData( ParametersCollection parameters ); protected abstract void UpdateData( ParametersCollection parameters ); protected abstract void DeleteData( ParametersCollection parameters ); public void Save() { /* Nell'applicazione reale la gestione è decisamente più complessa e comprende una serie di eventi che permettono di manipolare dall'esterno alcuni aspetti e consento anche di interrompere il processo di salvataggio e/o cancellazione */ ParametersCollection parameters = this.GetParameters(); /* Usiamo IsNew per determinare quale sia l'azione da intraprendere per il salvataggio */ if( this.IsNew ) { this.InsertData( parameters ); } else { this.UpdateData( parameters ); } } public void Delete() { ParametersCollection parameters = new ParametersCollection(); parameters.Add( "UID", this.UID ); this.DeleteData( parameters ); }}//// ASSEMBLY// Topics.MyApplication.Domain.dll//public class MyCustomer : MyObject{ protected override void Initialize( System.Data.IDataReader reader ) { if( reader == null ) { /* L'istanza rappresenta un nuovo oggetto quindi inizializziamo i field con i valori di default */ this._companyName = String.Empty; } else { this._companyName = reader.GetString( reader.GetOrdinal( "CompanyName" ) ); } } public MyCustomer( System.Data.IDataReader reader ) : base( reader ) { } public MyCustomer() : base() { } private string _companyName; public string CompanyName { get{ return this._companyName; } set { if( value == null ) { value = String.Empty; } if( !value.Equals( this._companyName ) ) { this._companyName = value; } } } protected override ParametersCollection GetParameters() { ParametersCollection parameters = base.GetParameters(); parameters.Add( "CompanyName", this.CompanyName ); return parameters; } protected override void InsertData( ParametersCollection parameters ) { /* TODO Solo la classe "reale" Customer conosce il sistema per persistere/manipolare i propri dati */ } protected override void UpdateData( ParametersCollection parameters ) { /* TODO Solo la classe "reale" Customer conosce il sistema per persistere/manipolare i propri dati */ } protected override void DeleteData( ParametersCollection parameters ) { /* TODO Solo la classe "reale" Customer conosce il sistema per persistere/manipolare i propri dati */ }}
Persistence
LayerE' lo strato
che si occupa effettivamente di persistere ( Insert Update e Delete ) i
dati, anche qui un esempio...
Spesso e volentieri leggo sui vari NG di svariati problemi nel tentativo di
integrare finestre modali all'interno di una applicazione web.Innanzitutto
vorrei sottolineare che non considero la soluzione "Modale" in ambiente Web una
soluzione, il flusso delle informazioni in ambiente web deve essere rivisto al
fine di adeguarsi alle caratteristiche del web, i soli problemi di xBrowsing
dovrebbero farci dimenticare un sacco di cose.
Comunque in situazioni ben controllate, esempio tipico sono le intranet, la soluzione modale velocizza
di molto lo sviluppo e mantiene l'utente "legato" (lo ritento negativo) al
tradizionale sitema di imput e di browsing dei dati, classicamente "WindowsForms...