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.
Think about, case study, implementation... Pensieri, casi di studio e implementazioni, tutto quello che devo affrontare lavorando in c#.
martedì 18 dicembre 2007
Incapsulamento
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.
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 :
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
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.
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 :
- Form per inserimento
- Griglia per modifica e cancellazione
- Combo/lista per selezione di un utente
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
- Create una nuova soluzione di tipo web application
- aggiungete un nuovo progetto di tipo class library
- aggiungete come riferimento alla web application il class libray
- 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.
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.
Etichette:
Abstract,
C#,
Concept,
Fundamental,
Linguaggio
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:
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à.
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:
- GetName
- 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.
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.
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.
- Scrivere meno codice
- Scrivere un codice più finalizzato
- Modificare in modo più finalizzato
- Gestire in modo più accurato eventuali abilitazioni alle funzionalità
- Diminuire eventuali SLA per la gestione di Bug Fix
- Aggiungere nuove funzionalità con maggior semplicità
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:
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.
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.
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.
Iscriviti a:
Post (Atom)