Press "Enter" to skip to content

Librerie comuni – Un cambiamento strutturale ad una libreria di uso comune Note d’uso

Un articolo che illustra quello che spesso accade mentre sviluppiamo una applicazione, rileviamo un problema costruttivo e dobbiamo smontare qualcosa per ricostruire in un modo diverso. Credo che in edilizia si chiamino Varianti in corso d’opera, nello sviluppo di applicazioni sono esattamente la stessa cosa.

Dopo il  precedente post, che mostrava come creare un manager per i parametri di configurazione applicativi di un servizio, la prosecuzione logica dello sviluppo era quella di creare un progetto con un interfaccia utente per gestire questi parametri. Nei post precedenti abbiamo creato la struttura di base per trattare questo tipo di oggetti ed una interfaccia utente WPF che è in grado di gestirne una collezione creando dei controlli in modo dinamico in base al tipo di oggetto.

Il Problema

Nell’esempio che abbiamo spiegato nel post Generazione di Componenti da Codice in WPF, abbiamo costruito lo user control per modificare una collection di parametri di configurazione in modo tale che si agganci direttamente al File XML contenente la classe dei parametri serializzata, per fare questo, nel file XML abbiamo salvato tutta una serie di informazioni strutturali su come la UI deve costruire il necessario ad acquisire il parametro, la sua categoria, la sua descrizione, il tipo di editor da utilizzare, la maschera di input per i controlli numerici. E’ un approccio valido, ma ha un problema fondamentale, non è in grado di evolversi. Infatti, nei parametri di configurazione del servizio,  uno dei valori è una enumerazione, questo significa che dobbiamo introdurre un editor di tipo DropDownList fra quelli disponibili nella nostra UI ma a questo editor come passiamo la lista dei valori validi? Dobbiamo aggiungere alla classe DnwSetting un ulteriore property che ci permetta di inserire questa lista e serializzarla, in modo che il controllo della UI possa leggerla e di conseguenza creare il controllo e permetterci di aggiornare la UI.

La domanda che ci poniamo

Perché per salvare su un file una lista di tipo Nome – Valore dobbiamo memorizzarci una serie di informazioni relative a come dovrà essere creata la sua interfaccia utente? E se volessimo creare interfacce diverse in base al contesto (Una applicazione WPF funziona in modo diverso ad una applicazione Windows Forms o ad una applicazione ASP.Net) Salvare anche tutte le informazioni accessorie relative alla gestione dell’interfaccia utente non ci serve, anche perché farlo potrebbe risultare in una interfaccia utente con le descrizioni sbagliate, non potremo cambiare l’editor di un tipo di dato una volta creato, non potremo creare una nuova categoria ed assegnarvi vecchi valori. Ci stiamo quindi andando a infilare in un vespaio, non in questo momento ma probabilmente nei nostri sviluppi futuri.

Ci sono sempre due strade quando si scopre questo genere di  problemi. La più semplice, mettiamoci una pezza, la più corretta, modifichiamo quello che abbiamo sbagliato e cambiamo completamente strada.

Accorgersi di un problema potenzialmente deleterio quando si ha scritto migliaia di righe di codice spesso significa passare giornate di lavoro a rimediare, ecco perché quando si progetta una applicazione bisogna sempre riflettere bene su come separare quello che riguarda la presentazione (User Interface) da quello che riguarda il funzionamento (Business e Data Logic). Ma non sempre i problemi sono macroscopici come quello che abbiamo voluto introdurre noi. A volte sono molto più sottili e subdoli. Il consiglio che ci viene da  più di 25 anni di programmazione è:

Se lo trovate fermatevi, fate un backup di tutto quanto, testate la modifica su un elemento e validate la nuova modalità di lavoro, poi applicatela a tutto il vostro codice, usualmente questo ripaga nel tempo.

Se avete una serie di progetti vecchi, chiusi e conclusi con il problema e mettervi mano sarebbe troppo oneroso, create un nuovo progetto per le vostre librerie di base, cambiategli nome e versione e agganciate tutti i progetti in corso alle nuove librerie facendovi le modifiche, quando sarà il momento di riprendere in mano un vecchio progetto, convertitelo alle nuove librerie. Ci sarà del lavoro in più, ma ciò che avrete fatto per migliorare le cose, vi renderà la vita molto più facile nello sviluppo delle modifiche anche ad un vecchio progetto.

Ovviamente, tutto questo articolo non vuole essere un modo per dirvi, “Si deve fare così!”, ma è solo il punto di vista di chi ci è passato ed ha visto i risultati di tutti e due i possibili approcci. A volte queste necessità possono anche essere pilotate dal cambio del Framework, quando siamo passati dal framework 2.0 al framework 4.0, (saltando gli intermedi perché non sempre c’è il tempo di sperimentare tutto) ci siamo trovati ad avere a disposizione i metodi Extension, le funzioni introdotte da Linq, nuovi tipi di collezioni, generici e funzionalità. Sarebbe stato stupido conservare tutto il codice scritto nella forma ammissibile con il Framework 2.0 e perdere i vantaggi di quanto di nuovo introdotto, anche se è fattibile, l’approccio conservativo dice, implemento le cose in modalità 4.0 e le applico ai nuovi progetti, lascio quelli vecchi con il vecchio codice.

E’ una delle strade, noi abbiamo preferito dire: OK ci sono 5 modifiche che ci renderanno la vita più facile in tutta la stesura del codice? Bene, creiamo il codice nella versione nuova,  mettiamo obsoleti i metodi, le classi e le funzionalità nella vecchia versione, e facciamo un “trova e sostituisci” ovunque.

Non è sempre la cosa migliore da fare, non è sempre applicabile, ma nel nostro caso, quando l’abbiamo fatto, abbiamo perso del tempo inizialmente ma ne abbiamo guadagnato molto di più in seguito, anche facendo modifiche su progetti molto vecchi.

Prendete pure questo post con le dovute precauzioni, pensando bene alle soluzioni prima di costruirle e perdendo magari un po’ di tempo a fare dei test. Si può essere fortunati e non trovarsi in questo tipo di situazione, ma come dice Lupo Alberto, la fortuna è cieca, ma la sfiga ci vede benissimo. Non abbiate paura di tornare sui vostri passi, pure con le dovute precauzioni.

In questo tipo di situazione, l’uso di Team Foundation Server e delle sue funzionalità di Branch è sicuramente un valido appoggio per poter applicare questo tipo di modifiche con la possibilità di controllare la History del codice e verificare quello che stiamo modificando ed essere certi di non avere per errore eliminato qualche funzionalità o qualche controllo per errore.

La nostra soluzione

Per risolvere il problema che abbiamo creato eseguiremo una serie di modifiche strutturali alle librerie e alla user interface di base, poi proseguiremo con lo sviluppo, ecco che cosa faremo:

  • Elimineremo dai dati salvati in XML per la classe DnwSetting tutti i campi riguardanti la User Interface, quindi lascieremo solo ID e Valore.
  • Modificheremo lo User control per la modifica di una collezione di DnwSetting in modo che non abbia una interazione diretta con il File XML contenente i dati ma invece riceva dal manager della User Interface una collezione di DnwSetting che sarà stata creata secondo le esigenze del progetto.

Le modifiche a DnwSetting

[XmlAttribute]
public string ID
{
	get
	{
		return mID;
	}
	set
	{
		mID = value;
		OnPropertyChanged(FLD_ID);
	}
}

[XmlElement]
public string Value
{
	get
	{
		return mValue;
	}
	set
	{
		mValue = value;
		OnPropertyChanged(FLD_Value);
	}
}


[XmlIgnore]
public string Category
{
	get
	{
		return mCategory;
	}
	set
	{
		mCategory = value;
		OnPropertyChanged(FLD_Category);
	}
}


[XmlIgnore]
public string Description
{
	get
	{
		return mDescription;
	}
	set
	{
		mDescription = value;
		OnPropertyChanged(FLD_Description);
	}
}

[XmlIgnore]
public EditorType EditorType
{
	get
	{
		return mEditorType;
	}
	set
	{
		mEditorType = value;
		OnPropertyChanged(FLD_EditorType);
	}
}

[XmlIgnore]
public int Position
{
	get
	{
		return mPosition;
	}
	set
	{
		mPosition = value;
		OnPropertyChanged(FLD_Position);
	}
}

[XmlIgnore]
public string Mask
{
	get
	{
		return mMask;
	}
	set
	{
		mMask = value;
		OnPropertyChanged(FLD_Mask);
	}
}

[XmlIgnore]
public DnwDropDownItemsCollection DropdownValues
{
	get
	{
		return mDropdownValues;
	}
	set
	{
		mDropdownValues = value;
		OnPropertyChanged(FLD_DropdownValues);
	}
}

Le modifiche sono state le seguenti:

  • Abbiamo lasciato serializzare le property ID come Attributo e Value come elemento del file XML
  • Abbiamo inserito il parametro XmlIgnore su tutte le altre property
  • Abbiamo aggiunto una property per la gestione della lista dei valori per una dropdown.

Le modifiche a SettingsPage

Abbiamo modificato anche lo User Control per la gestione dei Settings:

public partial class SettingsPage : UserControl
{

	private DnwSettingsCollection mSettings;

	public SettingsPage()
	{

		InitializeComponent();
	}

		public void Init(DnwSettingsCollection settingsToManage)
		{
				mSettings = settingsToManage;
				this.DataContext = this;
				GenerateControls();
		}

Le due modifiche primarie alla classe sono

  • l’eliminazione del View Model, che risultava inutile, in quanto abbiamo tolto dalla classe la necessità di modificare il File XML dove i parametri applicativi sono memorizzati. Pertanto non ci serve un model vero e proprio, metteremo in binding il controllo stesso che fornisce allo XAML la collection dei settings.
  • La modifica del metodo Init a cui passiamo una DnwSettingsCollection invece di passare un nome di file.

Abbiamo anche implementato il metodo per generare un controllo di tipo ComboBox per gestire i parametri di configurazione con una lista a discesa, la discussione dell’intera implementazione è disponibile nell’articolo:

l codice del progetto di esempio relativo alle librerie modificate è disponibile al link qui sotto: