martedì 18 dicembre 2007

Incapsulamento

Questo concetto di base ha lo scopo di "nascondere" o di "rendere inaccessibile" il funzionamento di una porzione di codice.
L'incapsulamento lo si definisce in fase di progettazione (verosimilmente nell'Analisi Tecnica) e nella maggior parte dei
casi consente di estendere il codice o di modificarlo senza coinvolgere chi lo usa.

Ora con questa spiegazione si rischia di comprendere troppo poco, ma posso garantirvi che il concetto più vicino a questo
è la programmazione a plugin.

A tale proposito è necessario un esempio, perchè come al mio solito "credo che la pratica sia a volte molto più esplicativa
della teoria".

Supponiamo di trovarci in un concessionario d'auto pronti a richiedere il nostro preventivo...
(10 anni fa ci avrebbero dato in mano una Brochure e raccontato tante belle cose, oggi in qualche concessionario le cose
sono un po' diverse).
Il venditore ci mostrà a video una serie di opzioni, le nostre scelte implicheranno poi quanto sarà realmente realizzato.

A questo punto ci spostiamo nel sorgente di quello che stiamo guardando, per meglio comprendere che cosa vuol dire
incapsulamento.

La casa produttrice del software per la personalizzazione di un veicolo, ha pensato in grande supponendo che agli inizi
avrebbero prodotto due tipi di veicolo, ipotizzando però di poter arrichire la loro produzione in futuro.

Hanno studiato quindi una serie di modelli che "universalmente" o quasi potessero descrivere le caratteristiche base
di un veicolo e ne venne fuori questo:

//
// interfaccia per il motore
//
public interface IMotore
{
int Cilindri {get;}
float Potenza { get; }
bool Turbina { get; }
Rectangle Ingombro { get; }

void Accendi();
void Spegni();
void Frena();
void Accelera();
}

//
// interfaccia per la trazione
//
public interface ITrazione
{
int Rapporti { get; set; }
int posizione { get; set; }

void AumentaMarcia();
void DecrementaMarcia();
void Folle();
void RetroMarcia();
}

//
// interfaccia per gli allestimenti
//
public interface IAllestimenti
{
bool InterniInPelle { get; set; }
bool InterniInEcoPelle { get; set; }
bool InterniInTessuto { get; set; }
bool InterniInTessuto_Pelle { get; set; }
bool InterniInTessuto_EcoPelle { get; set; }
bool FinitureAlluminio { get; set; }
bool FinitureLegno { get; set; }
bool FinitureRadica { get; set; }
bool FinitureLaccatoLucido { get; set; }
bool FinitureLaccatoOpaco { get; set; }
bool FinitureSatinato { get; set; }
bool LettoreCD { get; set; }
bool LettoreDVD { get; set; }
bool Navigatore { get; set; }
bool Dolby5 { get; set; }
bool Dolby7 { get; set; }
int Airbags { get; set; }
}


//
// classe del veicolo
//
public class Veicolo
{
public IMotore Motore;
public ITrazione Trazione;
public IAllestimenti Allestimenti;

//
// molto altro ancora...
//
}

Ancora una volta ci tengo a precisare che si tratta di esempi che potrebbero naturalmente non rappresentare
la pura realtà, e lo spero in parte.

A questo punto alla società sarebbe bastato realizzare tante librerie (esterna a quella di base) in cui
realizzare gli elementi del veicolo.

E quindi furono realizzate le prime due motorizzazioni:

public class MotorizzazioneBase : IMotore
{

#region IMotore Members

public int Cilindri
{
get { return 4; }
}

public float Potenza
{
get { return 78.5f;}
}

public bool Turbina
{
get { return false;}
}

public Rectangle Ingombro
{
get { return new Rectangle(0, 0, 200, 90);}
}

public void Accendi()
{
// quanto necessario per accendere
// inizia a far fluire il carburante
// invia scintilla a spinterogeno..

}

public void Spegni()
{
// quanto necessario per spegnere
//
}

public void Frena()
{
// prima che sia troppo tardi!
}

public void Accelera()
{
// Se no arrivi tardi...
}

#endregion
}

public class MotorizzazioneSport : IMotore
{

#region IMotore Members

public int Cilindri
{
get { return 6; }
}

public float Potenza
{
get { return 95.7f; }
}

public bool Turbina
{
get { return true; }
}

public Rectangle Ingombro
{
get { return new Rectangle(0, 0, 230, 90); }
}

public void Accendi()
{
// inizia a far fluire il carburante
// invia scintilla a spinterogeno..

}

public void Spegni()
{
// quanto necessario per spegnere
}

public void Frena()
{
// prima che sia troppo tardi!
}

public void Accelera()
{
// Se no arrivi tardi...
}

#endregion
}

A questo punto tutta la logica ( o le proprietà ) appartengono tutte ad una libreria, mentre la presentazione
dei dati risiede in un altra...

A tal proposito è possibile comprendere che variando i valori, il comportamento di MotorizzazioneSport, nella
classe Veicolo non saranno necessari interventi e per altro Veicolo non avrà precezione di cio' avviene all'interno
di MotorizzazioneSport.

Custom user control

Sprecherei ancora qualche parola su questo argomento, cercando più che altro di ragionare sulla funzionalità intesa come astrazione.

Il fatto di creare un "cuc" (quanto mi piace questo termine) da sicuramente del valore aggiunto.

Osservando questo schema:

si noti, che non è difficile creare un template di controlli, ed ereditarlo di volta in volta, dando una strutturazione all'applicazione molto efficiente.

Rimane pur vero che è necessario approfondire considerevolmente l'analisi tecnica al fine di non creare oggetti inutili o poco fruibili.

Va da se ogni controllo dovrà rappresentare una singola funzionalità e non un gruppo complesso di operazioni.


In questo esempio vorrei finalizzare i controlli in modo tale da creare una griglia, un form e combo, senza tuttavia dare una specializzazione effettiva delle classi.
Ma ereditando esclusivamente il nostro controllo template.

E' possibile inoltre estendere considerevolmente le funzionalità di un controllo implementando vari metodi, proteggendoli qualora fosse necessario, o creando metodi privati ad hoc per la funzionalità che il controllo rappresenta.



In questo esempio CostomControlGrid nel metodo render Disegnerà una griglia sulla nostra pagina, nessuno (anzi è lo scopo finale) ci vieta di implementare un metodo che popoli le colonne, dando le corrette intestazioni, e che naturalmente popoli le celle con i dati.

Come non smetterò mai di dire si tratta di un approccio dispendioso dal punto di vista della scrittura del codice, ma a vantaggio della qualità del nostro lavoro finale.

Web user control vs Custom user control

Argomento non semplice... quando conviene usare i "wuc" e "cuc" ?

Web user control
Comunemente noto come "questa porzione di pagina la userò in più pagine se non in tutte" viene generalmente usato per molteplici scopi. Ha tante valenze, potrebbe essere sfruttato per dare le intestazione alla pagina, per realizzare i menu', e tutto quello che più comunemente può apparire in più punti della vostra web application.

Per fare un esempio:
In un anagrafica utenti potremmo individuare :
  1. Form per inserimento
  2. Griglia per modifica e cancellazione
  3. Combo/lista per selezione di un utente
Il form potrebbe essere reso con un web user control che propone le text box necessarie all'inserimento
la griglia potrebbe essere una gridview con item template per la modifica e la cancellazione degli elementi
la combo potrebbe essere una combo box riempita con tutti gli elementi dell'anagrafica.

Ogni uno di questi elementi potrebbe essere riutilizzato all'interno del nostro applicativo, creando così una
univocità di chi svolge tutto quello che riguarda gli utenti.

Una pagina per l'associazione di utente a dei gruppi potrebbe ad esempio contenere solo il combo degli utenti
senza dover reimplementare lo stesso codice.

Chiaramente questo consente un ottima gestione del proprio applicativo, e finalizzarebbe gli interventi in caso di modifica senza dover agire in più punti diversi.

Ma in tutto questo potrebbe verificarsi un "fastidio" il codebehind ... e a meno che non ci siano altre possibilità questo codice potrebbe essere letto, e peggio ancora modificato da chi non dovrebbe.

Intendo espressamente il fatto che se realizziamo un applicazione per un cliente, tutto il codebehind è tutto in chiaro,
consentendo l'interpretazione del sorgente da parte di altri.

Tutto questo può essere dannoso, sia per i dati, sia per il destino della nostra applicazione.

Custom user control
Questo è un concetto un po più emblematico... comporta sicuramente la scrittura di molto più codice di un web user control, ma ci consente di creare una struttura più protetta e più completa.

Protetta perché alla fine del nostro custom user control avremmo un compilato.
Completa perché al netto del nostro applicativo ci sarebbe ben poco da vedere da parte di chi non deve.

Giusto per fare un esempio di custom control

  1. Create una nuova soluzione di tipo web application
  2. aggiungete un nuovo progetto di tipo class library
  3. aggiungete come riferimento alla web application il class libray
  4. aggiungete questa classe:

using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace CustomControls
{
public class mControl:Control
private string _spanText ="inserire qui il testo";

public virtual string spanText

{
get {return _spanText;}
set {_spanText = value;}
}

protected override void Render(HtmlTextWriter writer)
{
writer.Write("< span>"+ spanText +" </span>");
}
}


E' necessario aggiungere alla class library i riferimenti a : system.web, system.drowing.

Ora compilate, e troverete nella barra degli strumenti il vostro nuovo Custom user control... eseguite il drag n'drop
del vostro controllo nella vista di design della pagina. Ed ecco fatto.

Nella vostra applicazione vi ritroverete solo e soltanto il controllo e nessun codice leggibile o interpretabile da altri.

Chiaramente questo è un gran vantaggio, ma risulta molto complesso interpretare tutta un'applicazione strettamente basata su "cuc", questo perché il dispendio di energie per questa realizzazione è sicuramente notevole.

Facile o difficile la scelta spetta sempre a noi, dobbiamo individuare fino a che punto l'utente finale potrebbe avere le competenze necessarie a modificare i nostri sorgenti, o interpretarne le logiche.

Per quanto mi riguarda credo che questa via sia un buon compromesso per sfruttare al meglio una programmazione ben strutturata.

Un esempio fra tutti, questo modalità consentirebbe con maggior facilità la gestione di plugin anche nell'ambito web.

Polimorfismo

Nella maggior parte dei casi utilizziamo questo concetto senza averne la piena percezione.
Alla lettera polimorfismo vuol dire "aver molte forme", questa informazione anche se "open",
implica che la capacità di assumere forme di verse potrebbe dipendere dall'oggetto stesso,
o da chi lo crea.

Da qui in avanti il discorso potrebbe prendere due direzioni ben distinte, la prima è del
tutto filosofica e concettuale, dove l'avere molte forme potrebbe implicare il "quando" o
"per quanto". Oppure sfociare in un esempio pratico.

La definizione "avere molte forme" nasconde il fatto che la base potrebbe avere un forma
ma non essere del tutto completa o "completabile".
E' quindi necessario ricorrere per l'implementazione di questo esempio ad una classe di tipo
Asrtatta, in modo da fornire un indicazione dei metodi o un base degli stessi, che potranno o
dovranno essere estesi.

In molti sul Web propongono come Esempio "le figure giometriche" che rendono molto bene
l'idea del funzionamento del polimorfismo, di contro preferisco fare un esempio legato ad
un contesto già più evoluto.

L'idea che vogliamo implementare ha quest'aspetto :



//
// classe Entity
// Rappresenta un Utente
//
public class UserApplicationCredential
{
#region Private
private string _Name = "";
private string _Surname = "";
private string _Login = "";
private string _Password = "";
#endregion

#region Public
// read only
public string Name
{
get { return _Name; }
}
// read only
public string Surname
{
get { return _Surname; }
}

public string Login
{
get { return _Login; }
set { _Login = value; }
}

public string Password
{
get { return _Password; }
set { _Password = value; }
}
#endregion

#region Builder
public UserApplicationCredential(string sName, string sSurname)
{
this._Name = sName;
this._Surname = sSurname;
}
#endregion
}

//
// classe Abstract
// classe che diverrà polimorfica.
//
public abstract class DefaultUser
{
// rappresentazione interna delle credenziali
public UserApplicationCredential _UserCredential;

// processo di creazione di un utente
public abstract UserApplicationCredential UserCredential(string sName, string sSurname);

// processo di verifica
public abstract bool Exist();

// logica di Validazione
public abstract bool DoLogin(string sLogin, string sPassword);
}

//
// classe di validazione utente per DataBase
//
public class DataBaseUser:DefaultUser
{

public override UserApplicationCredential UserCredential(string sName, string sSurname)
{
return new UserApplicationCredential(sName, sSurname);
}

public override bool Exist()
{
//
// logica di ricerca
//
if ((_UserCredential.Name != "") && (_UserCredential.Surname != ""))
{
return FindUser();
}

return false;
}

public override bool DoLogin(string sLogin, string sPassword)
{
//
// logica di validazione
//
return true;

}

private bool FindUser()
{
//
// Si connetterà al DB
// Eseguirà la Query di ricerca
// Tornerà true o false se ha trovato l'utente.
//

return true;
}
}

//
// classe di validazione utente per LDaP
//
public class LDaPUser : DefaultUser
{

public override UserApplicationCredential UserCredential(string sName, string sSurname)
{
return new UserApplicationCredential(sName, sSurname);
}

public override bool Exist()
{
//
// crea un oggetto Active Directory
// Esegue la query di Ricerca
// Distrugge l'oggetto di Actice Directory
//

return true;
}

public override bool DoLogin(string sLogin, string sPassword)
{
//
// crea un oggetto Active Directory
// Esegue la query di Ricerca per tutti i campi
// Distrugge l'oggetto di Actice Directory
//

return true;
}
}

//
// classe di validazione INTERNA all'applicazione
//
public class ApplicationUser : DefaultUser
{
//
// non l'ho implementato ma si suppone che quest'array
// si popolato dal costruttore.
//
private UserApplicationCredential[] _Users = new UserApplicationCredential[10];


public override UserApplicationCredential UserCredential(string sName, string sSurname)
{
return new UserApplicationCredential(sName, sSurname);
}

public override bool Exist()
{
//
// cerca Name e Surname
//
for (int i = 0; i < _Users.Length; i++)
{
if (
(_UserCredential.Name == _Users[i].Name) &&
(_UserCredential.Surname == _Users[i].Surname)
)
{
return true;
}
}
return false;
}

public override bool DoLogin(string sLogin, string sPassword)
{

//
// cerca per tutti i campi
//
for (int i = 0; i < _Users.Length; i++)
{
if (
(_UserCredential.Name == _Users[i].Name) &&
(_UserCredential.Surname == _Users[i].Surname) &&
(_UserCredential.Password == _Users[i].Password) &&
(_UserCredential.Login == _Users[i].Login)
)
{
return true;
}
}
return false;
}
}

Che cosa abbiamo creato ?
Una classe entità il cui scopo è quello di avere una definizione comune di utente per tutte le classi.

Una classe astratta che rappresenta la base dell'oggetto polimorfico.

Tre classi che ereditano l'oggeto base e compio azioni Differenti fra loro per la validazione dell'utente.

Attenzione Queste classi in realtà non fanno nulla si tratta solo di esempi molto sbrigativi.

Adesso passiamo al main o all'entry point del nostro applicativo.

class Program
{
static void Main(string[] args)
{
string userName = "";
string userSurname = "";
string userLogin = "";
string userPassword = "";

Console.WriteLine("Insert Name and Surname");
userName = Console.ReadLine();
userSurname = Console.ReadLine();


DefaultUser _DefUser;

//
// nessun parametro usiamo gli utenti dell'applicazione
//
if (args[0]=="")
{
_DefUser = new ApplicationUser();
}

//
// è stata richiesta una validazione Ldap
//
if (args[0].ToLower() == "-ldap")
{
_DefUser = new LDaPUser();
}

//
// è stata richiesa la validazione su DB.
//
if (args[0].ToLower() == "-db")
{
_DefUser = new DataBaseUser();
}

//
// necessaria (forse no)
//
if (_DefUser != null)
{
_DefUser.UserCredential(userSurname, userSurname);

if (_DefUser.Exist())
{
Console.WriteLine("Insert Login and Password");
userLogin = Console.ReadLine();
userPassword = Console.ReadLine();

_DefUser.DoLogin(userLogin, userPassword);
}
}
}
}

Che cosa abbiamo fatto ?
Abbiamo ipotizzato che per la nostra suluzione l'utente debba per prima cosa immettere il nome ed il cognome.
Quindi verifichiamo negli argomenti se è stata specificata una modalità di validazione dell'utente...

Ed è proprio nell'atto di "create" dell'oggetto _DefUser decidiamo il tipo di validatore che utilizzeremo.

In conclusione l'oggetto _DefUser può assumere aspetti di concetto simili ma di comportamento differente.

lunedì 17 dicembre 2007

Plugin

Si certo perchè il discorso non è ancora finito... chiaramente.

Si supponga di dover creare un applicazione che carica autonomanente i propri plugin, si supponga inoltre che questi risiedono esclusivamente in un unico folder noto all'applicativo (dicitura presente nelle configuarazioni)

Si supponga inolte che il plugin abbia (debba avere) fondamentalmente due possibili metodi:
  1. GetName
  2. Show

il primo restituisce il nome del plug, mentre il secondo consente l'attivazione. E' possibile che serva molto altro rispetto quanto detto, ma queste sono sicuramente le fondamentali.

E' quindi il caso di creare un Interfaccia in modo che tutti i plugin debbano per forza avere questi metodi.

Chiaramente il plugin stesso avrà molte funzionalità in più, ma saranno esclusivamente del plugin, e quindi finalizzate alla funzionalità del plugin stesso.

Ci sarà quindi un tipo base di plugin a cui tutti i nuovi plugin dovranno fare riferimento, e che non da meno
esporrà forzatamente l'interfaccia GetName e Show.

Non da meno c'e' da considerare il fatto che non si stia facendo il caricamento di un solo elemento, ma di una vera
e propria collezione, ossia un insieme di elementi fisici e logici Tipizzati come plugin.

In questo modo sarà possibile in ogni momento accedere a questa lista ed eventualmente effettuare ogni possibile
valutazione (vedi abilitazioni per utente).

Il loader, ossia chi si occupa di individuare i plugin e caricarli.

Il manager chi si occupa di trasformare i nostri plugin.



Alla fine dovreste ottenere qualcosa non troppo distante da questo.

Si tratta di un esempio,che serve esclusivamente a capire che una struttura come questa non è semplice da implementare a posteriori.

Perchè evidentemente gli impatti possono essere troppi e troppo costosi da gestire.

E' quindi fondamentale effettuare parecchio studio prima di creare un approccio approssimativo. E' fondamentale comprendere se questo tipo di implementazione è davvero necessario.

Va de se che un applicazione piccola, single user, e strutturata su misura per un utente, potrebbe non richiedere questa strutturazione.

Ma tutto questo non è del tutto sufficiente.

Consideriamo che i plugin sono semplicemente elementi che vengono individuati tramite una lettura di una directory, e se rispecchiano le regole (come ad esempio un prefisso plg_ ) vengono associati a delle voci di menù.

E' corretto pensare che fino a questo punto nessuna delle classi che ho elencato si occupa di gestire l'effettiva gestione
della voce di menu e quindi di caricare effettivamente i plugin.

Quindi facendo un analisi più approfondita si otterrebbe qualcosa di simile a questo schema:

Dove IPlugin rimane comunque l'interfaccia, a tutti i plugin che implementerò ( vedi customPlugin)
Dove il plugin Manager si occuparà interamente delle gestione degli elementi
Dove il plugin Factory si occuperà di leggere l'elenco di plugin.

*
Sarà inoltre compito del plugin manager caricare ed eseguire il metodo show del plugin.
si noti il metodo DoPluginAction a tutti gli effetti è delegato all'azione del menù
newmenuItem.Click += new EventHandler(DoPluginAction);

Certo il concetto non è semplice. Si tratta come scopo ultimo di creae un applicazione che sia capace di individuare in autonomia tutto ciò che gli vine aggiunto a run time.

Ad ogni restart nell'applicazione verranno individuati i nuovi plugin e caricati nell'apposito menù.

Ci sono parecchi vantaggi nello scrivere un applicazione a plugin, come mi pare di aver già accennato il primo fra tutti e che una volta scritti codice e regole per il caricamento, con questo metodo ci basta solo implementare l'interfaccia iPlugin nella nostra nuova funzionalità-

Fra gli svantaggi posso sicuramente annoverare la scarsa possibilità di creare nidificazione fra funzionalità.
Gli utenti per esempio saranno finalizzati a loro stessi ... questo potrebbe essere un problema ma il tutto dipende
da come analizzare e implementare le varie funzionlità.

sabato 15 dicembre 2007

Plugin

Questo termine sta prendendo sempre più piede.. diciamo che è una moda nata parecchi anni fà quando ci si era resi conto che i clienti oggi chiedevano A domani A+B dopo domani A+B+C.

E spostandoci su una linea temporale un pò più ampia si arrivava ad avere la somma dell'intero alfabeto (comprese lettere cirilliche).

Questo in un primo momento venne interpretato come "è stata fatta male l'analisi iniziale".
Col passare del tempo divenne "prevenire è meglio che curare".

Sta di fatto che studiare un'applicazione fatta a "plugin" consente parecchi vantaggi.

  1. Scrivere meno codice
  2. Scrivere un codice più finalizzato
  3. Modificare in modo più finalizzato
  4. Gestire in modo più accurato eventuali abilitazioni alle funzionalità
  5. Diminuire eventuali SLA per la gestione di Bug Fix
  6. Aggiungere nuove funzionalità con maggior semplicità
E' anche vero però che strutturare un applicazione a plugin richiede un bel pò di dimestichezza con il concetto di plugin stesso.

La mia personalissima definizione, di plugin è :
un qualcosa che interagisca con l'applicativo padre e che tuttavia rimanga il più possibile autonomo nella gestione di quello in cui è specializzato.

Per fare un esempio abbiamo l'applicativo calcolatrice, che esegue le principali 4 operazioni.
Un plugin potrebbe essere la radice quadrata.

Scrivere meno codice
Questo è vero fino ad un certo punto, ma in buona sostanza i plugin sono e rimangono piccoli applicativi che si occupano di svolgere funzioni specializzate.
Si scrive molto probabilmente meno codice perchè si risce a creare un maggior disaccoppiamento fra l'applicazione padre ed il plugin. (quindi) Il plugin stesso è fatto da poco codice, anche se
l'applicazione padre dovrà contere il codice necessario per dialogare con il plugin.

Scrivere un codice più finalizzato
Questo è sicuramente il punto più importante della programmazione a plugin... si specializzano le funzionalità in modo che queste non debbano essere ripetute in altre parti dell'applicativo.
L'anagrafica degli utenti, per esempio, deve essere una funzionalità unica all'interno di un applicazione, e deve essere sempre richiamata in modo da non dover replicarne il codice.
Realizzare un anagrafica utenti tramite plugin consente inoltre di poter dare, all'applicazione padre, la possibilità di gestire la profilazione abilitando o meno quella funzionalità.

Modificare in modo più finalizzato
Quante volte abbiamo dovuto ricompilare TUTTO ?? Già abbiamo fatto il nostro bravo applicativo, ci vengono richieste delle modifiche su una funzionalità. E noi dobbiamo
-rimettere mano al codice di tutta l'applicazione
-verificare tutte le funzionalità che collidono con quanto abbiamo fatto (regression)
-generare un nuovo test book sulla regression.. e via via via ...
In questo caso, se è stata fatta una strutturazione concreta, non c'e' bisogno di approfondire così tanto il nostro intervento.

Dobbiamo modificare il plugin, agire solo su di lui, e non dobbiamo fare una regression totale,
ma concentrarci solo su una minor porzione di codice.
(certo il test book serve lo stesso, ma non occorre verificare tutta l'applicazione...)

Gestire in modo più accurato eventuali abilitazioni alle funzionalità.
Posto l'esempio di un applicazione dedicata ad un ufficio strutturato (manger, leaders, developers)
potrebbe essere necessario creare delle profilazioni con realativi livelli abilitativi.
Un applicazione realizzata a plugin consente una notevole facilitazione, l'abilitazione potrebbe essere resa proprio tramite il processo di caricamento dei plug.
Rendendo così l'applicativo dinamico, in base ai profili fin già dal caricamento dell'applicativo stesso.
Si pensi ad un prodotto di consuntivazione dove:
developer > compila il rapportino
leader > verifica stato di compilazione, e report giornaglieri
manger > controlla lo stato dei vari leader , dei progetti e dei report.

Chiaramente una corretta profilazione consete ad ogni tipologia di utenza di non vedere ciò
che non gli è consentito.

Diminuire eventuali SLA per la gestione di Bug Fix
Gli SLA... siamo tutti affetti da questo male... o questo bene.
Intervenire su una sola funzionalità difettosa non coincide con l'intervenire su tutto un applicativo. Questo è un dato di fatto indiscutibile.

Aggiungere nuove funzionalità con maggior semplicità
Mentre questa è vera e propria filosofia, ma è anche quanto di più vicino alla realtà.

In un applicazione fatta a plugin è molto più semplice creare una nuova funzionalità limitando notevolmente gli impatti su tutto il resto dell'applicazione, e non solo.
Questo consente anche il vantaggio di gestire le nuove funzionalità in completa autonomia rispetto al resto dell'applicazione.

Ogni nuova funzionalità realizzata tramite un plugin dovrà essere vista come un nuovo progetto,
e naturalmente dovrà presentare quanto necessario per essere individata dal padre e qundi usta.

Perchè Disaccoppiare?

Questa è davvero una domanda interessante. A che proposito è necessario creare disaccoppiamento? E che cosa va fondamentalemente disaccoppiato?

In C# e più o meno in tutta la sfera della programmazione a oggetti il disaccoppiamento è molto forte. Anche se viene data molta libertà a chi scrive il codice per creare/costruire la propria logica.

Quando si parla di disaccoppiamento generalmente ci si riferisce al rapporto fra i dati e chi li gestisce, tipicamente fra il Business logic e il Data Layer.

Si intenda creare un livello che permetta la comunicazione fra dati e logica, ma che renda trasparente il passaggio.

Supponiamo il caso di dover scrivere un applicativo che ha le anagrafiche su Db, ma tutto il
resto è realizzato tramite XML.

Nei precedenti approcci alla programmazione avremmo dovuto ipoteticamente creare due
connessioni la prima sul Db la seconda ai file XML. Gestendo di volta in volta tutte le azioni
correlate.

Naturalmente con il rischio di mantenere un codice poco efficente.

Certo avremmo potuto scrivere due belle DLL, e fare in modo che fossero loro ad eseguire
tutti comandi necessari alla gestione dei dati, e già questo sarebbe stato un buon passo avanti.

Questo perchè disaccoppiare in senso lato vuol anche dire proteggere i propri dati, nascondendo
le connessioni, nascondendo i percorsi avremmo già ottenuto un buon livello di protezione, ma si tratta già di disaccoppiamento?

In c# è possibile strutture la propria soluzione in modo da creare i livelli proposti nel Mvc,
e possibile ( lo era anche con vb ... ) creare dei progetti correlati e naturalmente usarli.

Supponiamo qundi una soluzione tipo la seguente:

CrossFire
prjDataLayer
prjBusinessLogic
prjPresentation-WA
prjPresentation-WF

Banalmente una soluzione con 4 progetti.

Il primo si occupa esclusivamente dei Dati
Il secondo di applicare le logiche sulle richieste utente
Gli ultimi due sono possibili presentation (WA > windows Application, WF > web Forms)

Parrebbe un ottima soluzione e lo è se la fonte dati è unica.

CrossFire
prjDataLayer
prjDataLayer.SQLSEVER
prjDateLayer.XML
prjBusinessLogic
prjPresentation-WA
prjPresentation-WF

Già in questo modo si può intuire che c'e' (ben visibile) una diversificazione fra le fonti dati.

A tutti gli effetti il DataLayer dialogherà con il BusinessLogic ridirigendo le richieste a uno
dei due DataLayer specializzati.

Chiarmente in questo caso tutte le definizioni dei dati dovrebbero trovarsi in DataLayer.
in modo che BusinessLogic non sappia assolutamente dove questi realmente risiedano,
e di conseguenza anche i due presentation non avrebbero alcuna percezione di chi
fornisca loro i dati.

Si comprende quindi l'importanza del disaccoppiamento >> esso è uno strumento che consente di rendere INDIPENDENTI in senso stretto i dati da CHI LI GESTISCE e da CHI LI MOSTRA.

Certo va da se che maggiore è l'accuratezza nel creae disaccoppiamento e maggiore è lo sfrozo nello scrivere il codice.

Codice che tuttavia potrà ben essere riutilizzato.

giovedì 6 dicembre 2007

Reuse

Quando applicare il concetto di reuse? Sempre?

Nell'esempio dell'automobile, il concetto di Reuse è esteso ad alcune classi che compongo l'auto, il loro scopo, ed il loro comportamento sarà sempre uguale, ed è quindi possible riutilizzare la stessa logica.

Parafrasando un esempio un po più complesso, si pensi ad un flusso anagrafico relativo a degli utenti.

Dove ci sarà sicuramente una classe relativa ad un singolo utente, un classe manager per gli utenti.



In tutto il nostro applicativo queste classi verranno usate in più di un occasione.

E' completamente insensato ed incoerente avere due classi che fanno la stessa cosa.

Avere due classi che per esempio fanno l'insert degli utenti in due modi diversi non solo è pericoloso ma anche complesso da gestire.



Supponiamo quindi di voler specializzare in l'inserimento dell'Amministratore. Non dobbiamo creare una nuova classe che implementi gli stessi metodi di tUtenti.

Ci baste estendere quella classe e finalizzare i medoti per la gestione dell'amministratore.

Chiarmamente tutte le scelte devo essere prese in base alla circostanze, se per scelta gli amministratori della nostra applicazioni si dovessero trovare in una fonte diversa da quella degli utenti.. bhe potrebbe risultare opportuno studiare un'approccio differente.

mercoledì 5 dicembre 2007

Reuse

Vi è mai capitato di dover scrivere più volte lo stesso codice? Ci sono dei casi specifici dove ciò è inevitabile, ma in molti casi risulta sconveniente, e troppo oneroso da gestire.

Scrivere del codice di tipo REUSE non è così semplice come sembra; anzi in molti casi risulta essere più dispendioso del riscrivere lo stesso codice più volte. Tuttavia ci sono una serie di implicazioni logiche da tenere sempre in mente.

Un esempio su tutti la classe auto.

public class tCambio
{
public int _Tipo_Cambio;
public int _Numero_Rapporti;
public int _Numero_Dischi_Frizione;
}

public class tMotore
{
public int _Tipo_Carburante;
public int _Numero_Cilindri;
public int _Emissioni_C02;
}

public class tDettaglio_Interni
{
public Color Colore_Primario;
public Color Colore_Secondario;
public Color Colore_Tappetini;
public Color Colore_Cappelliera;
}

public class tDettglio_Esterni
{
public Color Colore_Carrozzeria;
public Color Colore_Paraurti;
public Color Colore_Maniglie;
}

public class tAuto
{
public int _Numero_Ruote;
public int _Numero_Posti;
public tMotore _Motore = new tMotore();
public tCambio _Motore = new tCambio();
public tDettaglio_Interni _Interni = new tDettaglio_Interni();
public tDettglio_Esterni _Esterni = new tDettglio_Esterni();

public void Accendi()
{

}

public void Spegni()
{

}

public void Sterza()
{

}

public void Frena()
{

}

public tAuto()
{

}

}

Chiaramente si tratta di un esempio, nulla di concreto, però è quanto di più si possa avvicinare ad una descrizione di una macchina generica.

Come è possibile notare molti dei "pezzi" dell'auto sono stati realizzati esternamente alla classe, per semplice motivo,
che la classe motore può essere usata anche da altre entità indipendentemente dal fatto che siano auto o meno.

Una moto per esempio, potrebbe utilizzare nello specifico sia il motore che il cambio, ma a ben pensarci anche un motoscafo, e volendo un aero.

Decisioni, è nell'analasi tecnica che devono essere affrontate queste decisioni. E sicuramente devono essere fatte
prima di scrivere righe e righe di codice che potranno essere poco fruibili.

Naturalmente partendo da quest'esempio si può arrivare a considerare che questa classe non sia completa e che
a sua volta possa ulteriormente essere estesa, o migliorata.


public class MacchinaTruzza:tAuto
{
public string proprietario;
public string Marca;
public string Modello;
public string Numero_di_Serie;

public void Do_Sgommata_InStart()
{
}
public void Do_360_Destra()
{
}
public void Do_360_Sisnitra()
{
}
public void Do_Sgommata_inBreak()
{
}
}

Ecco quello che ne potrebbe venire fuori, estendendo la classe auto.

Rimarrebbero invariate le principali funzionalità della classe auto, ed in più vengono aggiunte altre funzionalità.

La mia prima classe..

public class tPrototipo
{
private string _Name;
public string Name
{
get { return _Name;}
set { _Name = value;}
}

public tPrototipo()
{
// void
}
}



Che cosa fa questa classe? Ma chi ha usato Delphi potrà subito farmi notare che sono un nostalgico. Eppure questa è stata la prima classe che ho scritto in C#.

Si tratta di una classe pubblica che espone un unica proprietà Name, ed implementa un costruttore vuoto.

Volendo questa classe può essere estesa da altre, o può essere utilizzata.

Facendo un ragionamento più filosofico, questa classe ha ben poco di utile,
il nome non ne chiarifica lo scopo
non ci sono commenti
non ci sono metodi

e tutto sommato serve a ben poco.

public class tUsaPrototipo
{
private tPrototipo _tpName;
public string Name
{
get { return _tpName.Name;}
}


public tUsaPrototipo(tPrototipo _tpCurrentName)
{
_tpName= _tpCurrentName;
}

}

La seconda classe chiaramente è stata qualcosa di un po più utile rispetto alla
prima. Ho semplicemente sperimentato quello che si poteva fare, provando a scrivere del codice che usasse la classe
appena implementata.

public class tExPrototipo:tPrototipo
{
public tUsaPrototipo(tPrototipo _tpCurrentName)
{
base.tpName= _tpCurrentName;
}

}

Discorso diverso per l'estensione.


Ce ne sono state molte altre prima di oggi, e non credo che le publicherò tutte.

martedì 4 dicembre 2007

Documentazione

Quante volte ci è stato chiesto di creare della documentazione... e quante volte non lo abbiamo fatto?

Sembra difficile da credersi, ma la documentazione, è altrettanto fondamentale almeno quanto lo è l'analisi, nella realizzazione di un progetto, scrivere non vuol solo dire "mettere i commenti", o mandare una mail indicando quello che si è fatto.

Nella realizzazione di un progetto ci possono essere svariati tipi di documenti e naturalmente ogniuno di loro può avere il suo scopo specifico.

Sicuramente ci devevono essere:
  1. Richiesta
  2. Studio di Fattibilità
  3. Analisi Funzionale
  4. Analisi Tecnica
  5. Stato Progetto
  6. Manuale Utente
  7. Casi di Test
  8. Rilascio
La Richiesta è quanto formalmente ha richiesto l'utente.

Lo Studio di Fattibilità, è un documento scritto tipicamente da un analista funzionale con la collaborazione,
qualora non vi fosse la conoscenza delle strutture di implementazione, di un analista tecnico.

Questo documento ha lo scopo all'interno del progetto di individuare se i mezzi, le possibilità di realizzare la
soluzione sia o meno fattibile. Saranno evidenziate possibili criticità che dovreanno essere discusse con
chi a formulato la richiesta.

E' possibile che questo documento si arricchisca più volte grazie al contributo del cliente e di chi sta seguendo
il progetto stesso.

Individuare e verificare gli obbietti è lo scopo principale di questo documento.

L'Analisi Funzionale, è la base del progetto stesso. Raccoglie quanto presente nella richiesta e nello studio di
fattibilità e crea la traccia da seguire per l'implementazione.

In questo documento vengono individuati gli attori del progetto, eventuali fasi di ralscio, e naturalmente
vengono elencate le funzionalità
vengono descritte le funzionalità
vengono date le specifiche di funzionamento
vengono elencate e descritte le strutture
viene esposto il gantt e l'effort per ogni risorsa

E' chiaro che tanto più è ricco questo documento tanto sarà più semplice redigere l'analisi tecnica.
Tipicamente questo documento ha una circolazione interna a chi svolge il progetto, anche se può essere esposto
in fase di elaborazione al Richiedente.

Documentare e condividere gli obbiettivi del progetto è lo scopo principe di questo documento.

L'Analisi Tecnica, è lo stadio precedente alla programmazione, senza di questo documento potrebbe essere impossibile procedere con la stesura del codice.

In molte realtà è possibile che questo documento sia a corredo dell'analisi funzionale, rischiando tuttavia di creare un testo ibrido che non riesca a esprimere ne i concetti tipicamente o dell'uno o dell'altro documento.

Tuttavia in questo testo vengono rappresentate le strutture (fonti dati) con le loro relative specifiche fin nel più semplice dettaglio. La logica di ogni singola funzionalità, con una descrizione del comportamento atteso, e inatteso.
E naturalmente come i dati e la logica saranno reppresentati.

Ancora una volta si parla di MVC, perchè appunto è questo che deve essere descritto, all'interno di questo documento.
Inutile a dirsi, che tanto più dettagliato sarà questo documento tanto più semplice sarà la realizzazione del codice.

Finalizzare il codice per raggiungere gli obbiettivi è la traccia di questo documento.

Lo Stato del progetto è il resoconto (giorno per giorno / fase per fase / step by step) di quanto è stato fatto e di quanto c'e' ancora da fare.
Potrebbe essere interpretato come una perdita di tempo, ma non lo è. Anzi l'importanza di questo documento, è sia per chi sviluppa, sia per chi si occupa di project management.

E' necessario sapere a che punto si è arrivati o quanto manca, in modo da stabilire se i tempi sono corretti, o per individuare eventuali problematiche.

Generalmente questo documento ritrae a scadenza, l'elenco delle funzionalità e la percentuale di completamento, con
eventuali note.

Il manuale utente ultimamente sembra essere demodè... ma è il nostro parafulmine... quanto meno un'ancora di salvezza. Potrebbe non essere utile scrivere che se si stacca la patch di rete mentre stiamo facendo un operazione transazionale su 5000 elementi non andrà a buon fine... perchè solo un idiota potrebbe verificarlo.

Ma gli utenti sono fatti così. Sono pieni di fantasia, di prove da fare inconsistenti...ed è per questo che nelle loro teste non c'e' spazio per le cose sensate.

Quindi se un applicativo non può fare una determinata cosa, questa va documentata nel manuale utente.

I casi di test vengono da se se abbiamo redatto un buon manuale utente.

Tipicamente sono l'elenco delle funzionalità e il loro comportamento, raccolti per quanto ci si aspetta dall'applicativo in base ad un input dato dall'utente.

Tipicamente "chi tocca i fili muore" .. e chi li tocca, li sfioro... l'abbiamo fatto tutti. Questo è un caso di test che deve essere raccolto fra le possibili funzionalità del prodotto.

Ogni Rilascio deve essere documentato in modo che il richiedente sappia che cosa gli è stato consegnato.
Ad ogni rilascio è necessario inoltre fornire il manuale utente su quanto è stato rilasciato, e il test case, in modo da poter consentire al richiedente di effettuare le verifiche sul progetto.


Chiaramente ci possono essere molti altri documenti, ma questi sono e rimangono i fondamentali, un progetto senza
tutto questo perde di consistenza, e probabilmente anche di efficenza.

Il che potrebbe allontanarci dai nostri obbiettivi.

Obbiettivi

In una soluzione, è necessario individuare gli obbiettivi, così com'e' fondamentale condividerli con chi si sta affrontando la soluzione.

Molte volte ci è capitato di affrontare delle suluzioni senza conoscere espressamente l'obbiettivo finale, e questo è un grosso problema, che crea pesanti conseguenze.

Immaginate la seguente richiesta... "ci serve un prodotto che faccia delle somme".
L'obbiettivo sembra chiaro, fare delle somme ma è davvero questo quello che l'utente finale vuole? e soprattutto è possibile condividere questo obbiettivo con altri?

Se lavorassimo in un Team, che cosa potremmo realizzare ? In questa richiesta mancano naturalmente tutto.

Che cosa deve sommare, (model)
come deve presentare le somme, (view)
come deve effettuare le somme (controller).

Approfondire gli obbiettivi è fondamentale, per la creazione del nostro modello. Naturalmente è d'obbligo ricondursi, al predicato MVC.

Lo scopo non è quello di perdere tempo, anzi è quello di guadagnarne finalizzando le nostre attività.

Se avessimo affrontato la richiesta, senza approfondire la reale esigenza, non avremmo potuto realizzare nulla.

Si noti questa richiesta... " da un pannello di immissione che presenta due campi editabili, che accettano solo valori numerici, l'utente premendo il tasto somma, ottiene un popup contenete il risulato della somma dei due valori immessi".

Certo si tratta di una richiesta più articolata e maggiormente definita.
C'è una definizione dei dati
c'e' una definizione della presentazione,
c'e' in fine una definizione della logica da applicare ai dati.

E' chiaro quindi che in fase di analasi non si può lasciare spazio alla superficialità, e che si debbano sempre approfondire le richieste iniziali.

Chiaramente c'e' sempre da stabilire un protocolo con il richiedente in modo che quanto richiesto persista fino al momento del rilascio.

"Va da se" che un obbiettivo può mutare nel corso del tempo, ma questa mutazione, deve essere sempre condivisa.
Se all'istante T0 la richiesta è una Bicicletta,
e all'istante T1 la richiesta è uno Scooter,
e' necessario che il Richiedente faccia circolare questa nuova necessità, in modo che chi sta producendo la
bicicletta, possa operare per apportare le modifiche, o ripartire con il progetto.

E' per questa tipologia di problemi che è necessario affrontare un lungo processo di analisi prima di partire con la progettazione.

Finalizzare, e conoscere gli obbiettivi, vi evita di montare tutti pezzi di uno scooter su una bmx ... sempre che questo
sia possibile...

Se si parla di costi in termini di tempo, immaginate la situazione appena descritta, si comprende con molta semplicità che lavorare ad un progetto non finalizzato, ci può costringere ad eseguire del lavoro doppio.

Al contrario uno sforzo iniziale considerevole nell'analisi, e nell'individuazione degli obbiettivi ci porterà alla soluzione, con un minor (si spera nessuno) numero di problemi.


Processo di analisi

L'analisi è probabilmente la parte più complessa, e più importante di tutta una soluzione.

Non è sempre semplice affrontare quest'argomento, perchè ci possono essere svariati argomenti correlati.

Se consideriamo "100" il punteggio totale attribuibile ad una soluzione di certo il peso dell'analisi è vicino a 80, naturalmente questo valore è float, e può muoversi sia verso valori più alti che verso valori più bassi.

E' scontato pensare, che se si deve scrivere un applicativo complesso con centinaia di classi che interagiscono fra di loro, che l'ago della bilancia punti maggiormente verso "scrittura del codice", ma questo non è assolutamente vero.

Maggiore è il tempo dedicato all'analisi, e minore sarà il tempo che lo sviluppatore/gli sviluppatori impiegheranno a realizzaare la soluzione.

Questo dipende anche da chi redige l'analisi...

E' chiaro che maggiori saranno le problematiche affrontate prima di scrivere il codice, e minori saranno le casistiche non gestite, che potrebbero apparire in fase di sviluppo.

L'analisi non si deve esclusivamente basare sul comprendere esclusivamente il problema, ma eventuali dipendenze.

Se il nostro obbiettivo finale fosse realizzare una calcolatrice, dobbiamo per prima cosa comprendere quali sono
i requisiti della calcolatrice, e non da meno che cosa si aspetta l'utente come output. In modo da creare una dettagliata documentazione da fornire a chi si occuperà di sviluppo.

MVC

Nella magior parte dei casi ci si imbatte in applicativi che leggono dati da un fonte, vengono modificati in base a delle logiche e quindi proposti all'utente finale.

Questo flusso tanto nautarale, quanto ovvio, è comunemente chiamato M.V.C.











M>Model
V>View
C>Controller

Il Model è l'application Object, il View è la Presentation e il Controller non è altro che la defenizione del modo in cui l'utente o gli utenti agiranno nei confronti dei primi due.

Già da questa semplice logica si può inture quanto questo approccio possa essere elastico ed applicabile a tutti
i contesti.

La riconducibilità di tutti i case study a questo approccio ci da la possibilità di individuare un comportamento
di base nell'affrontare un progetto.

il concetto di Model
Il model potrebbe essere rappresentato dai Dati ? In parte si.
I dati in questa astrazione, posso essere interpretati sia come semplici informazioni , che come informazioni complesse già derivate da una logica.

Tuttavia, si è soliti individura in MVC il model come dati anche se tuttavia si tratta di un astrazione.

il concetto di View
Con View si intende puramente la user interface, ossia che cosa viene presentato all'utente.
Possono esserci molteplici View per gli stessi dati.

Consentitemi un esempio, più o meno banale, ma alquanto esplicativo. Pensate all'indicatore del livello della benzina.
Questo è il nostro View, ed il Model è "il serbatoio + la benzina stessa + il galleggiante (sensore...)".

Per comprendere che siamo a quasi secco sono state implementate due user interface.
La prima è la lancetta che punterà indicativamente verso il basso.
La seconda è la spia luminosa che si accende.

I dati sono gli stessi ma sono rappresentati in modo completamente diverso.

Il concetto di Controller
Ritornando all'esempio fatto per il View il controller è in buona sostanza la logica per cui tutto debba funzionare.
Ossia il trigger che fa accendere la lampadina, quando il sensore rileva che siamo a secco, oppure, un azione correlata, dipendente dalla posizione della lancetta, quando questa raggiunge una determinata soglia, si accende ANCHE la lampadina.



Perchè assumere questo modello come il nostro punto di partenza?
Perchè è collaudato, perchè è diffuso e non da meno perchè funziona.
Devo essere onesto, quando sono passato dalla programmazione (giochi per bimbi) alla programmazione (giochi per grandi) mi sono reso conto che nella maggior parte dei casi quest'approccio non viene considerato, dando vita a problematiche che rimangono nascoste. E quando si presentano, (e si presentano sempre... ) sono difficili o troppo costose per essere recuperate.




Come cominciare ?

Presentandomi...

Mi chiamo Fabio Arosio, mi occupo di programmazione dal 96 circa. Attualmente ricopro la carica di Development Manager di una società di Milano.

Ho maturato buone esperienze nell'ambito dell'analisi e della programmazione.

Preferisco la programmazione a oggetti anche se il mi background è fortemente legato a Visual Basic, ma non nascondo il fatto di essere rimasto affascinato da Delphi.

Ho sperimentato qualcosa in Java, fino ad arrivare a linguaggio che più mi "gusta" C#.

E se mi è concesso, devo dire che di tante cose che Ms ha combinato questa mi sembra quella che gli è riuscita meglio.