domenica 17 gennaio 2010
Il post di Piergiorgio mi ha fatto venire in mente che a volte non ci sono le condizioni per avere successo in un progetto.
Per lavorare al meglio un team deve essere motivato ed un obiettivo chiaro e realistico concorre a tenere alta la motivazione.
Se l'obiettivo è poco chiaro, troppo lontano, troppo ambizioso il lavoro diventa routine, oppure ci si scoraggia perchè si pensa di non riuscire, oppure ci si annoia perchè non si vede mai la fine.
Quindi prima di iniziare un nuovo progetto è fondamentale avere ben chiaro quali sono le condizioni per avere successo.
Le seguenti domande potrebbero essere di aiuto:
- Chi finanzia il progetto? Qual'è il suo interesse nel progetto, perchè ha deciso di investirci dei soldi?
- Chi lo utilizzerà? Cosa si aspetta che faccia il software? Quali problemi gli risolve?
- Quali sono i tempi di consegna utili? Quali sono le funzionalità affinchè il software si possa dire completo e funzionante?
- Qual è la velocità di risposta del sistema affinchè non sia troppo lento?
La prima domanda è fondamentale: qualcuno sta investendo i suoi soldi per il software che dovremo sviluppare e quindi è importante capire il percnè investe questi soldi e quali sono le sue aspettative.
La seconda domanda serve per capire come rendere felici gli utenti, se gli utenti sono felici ne parleranno bene e l'investitore sarà soddisfatto dell'investimento.
Questi aspetti se trascurati possono portare ad un grosso spreco di denaro e frustrazione. Possono farci odiare il cliente ma la colpa potrebbe essere nostra perchè non gli stiamo dando valore, stiamo sviluppando qualcosa che pensiamo dia valore, ma nella realtà soddisfa solo il nostro ego.
L'emotività è una pessima consigliera, quindi è fondamentale basare le proprie scelte su dati oggettivi. Se non sono disponibili cercare di procurarseli. Quando si fanno delle scelte importanti sul progetto motivarle e cercare di essere il più possibile transparenti. La trasparenza è il primo tassello verso la fiducia.
Se un progetto va male potremmo esserne noi la causa, prima lo capiamo e prima potremo rimediare ai nostri errori.
Ho trovato molto istruttivo il video la cultura degli alibi.
lunedì 11 gennaio 2010

Ho sperimentato empiricamente sui progetto in cui ho lavorato che l'attività di design incrementale procede ad una velocità incostante.
Ci sono feature che riesco ad implementare velocemente e di cui sono soddisfatto del design ottenuto, mentre ce ne sono altre in cui faccio molta fatica per implementarle e con poca soddisfazione del risultato.
Kent Beck suggerisce di rallentare quando si verificano queste situazioni di attrito. Rallentare significa procedere per passi sempre più piccoli fino a riuscire a superare l'ostacolo.
Uno dei vantaggi del TDD è proprio quello di poter controllare la velocità: mi sento confidente? Vado veloce. Mi sento poco confidente? Rallento.
Quando mi trovo in queste situazioni un'altra tecnica che trovo efficace è quella di risolvere il problema in almeno due modi diversi. Sembra una perdita di tempo in realtà aumenta notevolmente la conoscenza del sistema e spesso evidenzia limiti del design che altrimenti non si noterebbero e che farebbero perdere molto più tempo in seguito.
Bisogna avere coraggio: se perdo confidenza nell'implementazione attuale è meglio fare revert e rifare la feature su cui si sta lavorando altrimenti si rischia di aumentare eccessivamente la complessità del sistema e di introdurre dei difetti.
Un'altra tecnica molto utile consiste nello spiegare cosa si sta facendo e quali problemi sto incontrando ad un altro membro del team; oppure, se la soluzione è sufficientemente generica, scrivere un articolo od un post sul proprio blog.
Trovo utile, banalmente, alzarmi dalla sedia fare quattro passi e ritornare a lavorare questo a volte porta ad una buona intuizione che semplifica il design e lo rende più malleabile.
E a voi vi capitano questi blocchi? Cosa fate per risolverli?
sabato 9 gennaio 2010
Come avevo scritto nel mio precedente post ho partecipato al workshop sul design emergente tenuto da Francesco Cirillo
Il concetto più importante che ho appreso è il seguente: quando si fa design focalizzarsi sul comportamento del sistema piuttosto che sulla struttura.
Cosa significa?
Partiamo da due rappresentazioni UML: class diagram e collaboration diagram.
Il class diagram è una rappresentazione della struttura delle classi, una rappresentazione statica che mal rappresenta il comportamento del sistema. Quindi se uso il class diagram per disegnare l'architettura del sistema risulterà poco tollerante ai cambiamenti, rigido.
Il collaboration diagram schematizza l'interazione tra gli oggetti. Vengono rappresentati i messaggi che si scambiano ed in questo modo si può rappresentare il comportamento del sistema a partire da uno scenario. Esempio di scenario: aggiunta di un prodotto al carrello.
Un esempio di collaboriation diagram lo trovate in questo articolo, mentre un software open source per disegnarli è starUML
Un approccio classico della programmazione ad oggetti è quella di creare oggetti che modellano il mondo reale ad esempio un software per la gestione del tornello di una metropolitana avrà gli oggetti tornello, biglietto, ecc. Questo rende poco riutilizzabile il codice perchè è legato al particolare problema che si sta risolvendo, invece, se gli oggetti rappresentano il comportamento il sistema risulta maggiormente malleabile ai cambiamenti.
Sono soddisfatto quindi di aver partecipato al corso perchè quello che ho illustrato secondo me è un concetto molto importante, che migliora il modo di scrivere codice. Quello che mi ha un pò deluso è l'assistenza post corso, Francesco non ha risposto alle domande che gli abbiamo inviato via mail.
mercoledì 30 dicembre 2009
Leggi la prima parte qui
Scelgo le ultime due colonne da cui partire per la valorizzazione in quanto sono le più semplici, infatti, ho un valore per ogni titolarità.
Abbiamo bisogno di:
- Creare le colonne Rendita e Valore
- Mappare le colonne con i campi RenditaCatastale e ValoreFabbricato della classe Titolarita
- Formattare il numero come 1.000,00
- Aggiungere una riga per ogni Titolarita
Partiamo dalla grid. L'implementazione attuale è la seguente e non fa ancora nulla:
public class Grid
{
public Cell this[int row, int col] { get { return null; } }
public List<ColumnHeader> ColumnHeaders { get { return null; } }
}
Come griglia userò una ListView alla quale dobbiamo impostare alcune property per farla funzionare come una grid:
- La prima colonna la facciamo invisibile in quanto si comporta diversamente dalle altre come documentato nelle msdn qui. In pratica viene ignorato il text align nella prima colonna.
- Dobbiamo settare la DetailsView
- Dobbiamo visualizzare le righe
- La selezione deve coprire tutte le celle di una riga
- dobbiamo poter aggiungere colonne e celle
Ecco il test per l'inizializzazione della grid:
[Test]
public void ShouldInitializeListView()
{
ListView listView = new ListView();
Grid grid = new Grid(listView);
grid.Initialize();
Assert.That(listView.Columns[0].Width, Is.EqualTo(0));
Assert.That(listView.View, Is.EqualTo(View.Details));
Assert.That(listView.FullRowSelect, Is.True);
Assert.That(listView.GridLines, Is.True);
}
Farlo passare è banale:
public void Initialize()
{
m_listView.View = View.Details;
m_listView.Columns.Add("").Width = 0;
m_listView.FullRowSelect = true;
m_listView.GridLines = true;
}
Aggiungiamo la possibilità di creare colonne:
[Test]
public void ShouldAppendColumnToListView()
{
m_grid.Initialize();
m_grid.AppendColumn("title");
Assert.That(m_listView.Columns[1].Text, Is.EqualTo("title"));
}
Aggiungiamo una riga vuota:
[Test]
public void ShouldAppendRowWithoutCells()
{
m_grid.Initialize();
m_grid.AppendEmptyRow();
Assert.That(m_listView.Items.Count, Is.EqualTo(1));
}
Ed ora una cella:
[Test]
public void ShouldAppendCellToTheLastRow()
{
m_grid.Initialize();
m_grid.AppendEmptyRow();
m_grid.AppendCell("some text");
Assert.That(m_listView.Items[0].SubItems[1].Text, Is.EqualTo("some text"));
}
A questo punto abbiamo bisogno di un data component processor che aggiunga una riga per ogni titolarità:
[Test]
public void ShouldAppendRowToTheGrid()
{
ListView listView = new ListView();
var processor = new RowAppenderComponentProcessor<Titolarita>(new Grid(listView));
processor.Process(new Titolarita());
Assert.That(listView.Items.Count, Is.EqualTo(1));
}
Formattiamo il numero decimale:
[Test]
public void ShouldFomatValue()
{
Money money = new Money(1234.56M);
string formatted = money.Format();
Assert.That(formatted, Is.EqualTo("1.234,56"));
}
Ci siamo quasi, manca ora un oggetto che prenda il valore del campo e lo formatti:
[Test]
public void ShouldAppendCellFormattingComponentValue()
{
ListView listView = new ListView();
Grid grid = new Grid(listView);
grid.AppendEmptyRow();
var processor = new CellAppenderComponentProcessor<Titolarita, Money>(
titolarita => new Money(titolarita.RenditaCatastale),
grid);
processor.Process(new Titolarita {RenditaCatastale = 1000M});
Assert.That(listView.Items[0].SubItems[1].Text, Is.EqualTo("1.000,00"));
}
Di questo riporto anche l'implementazione perchè forse non risulta ovvia:
public class CellAppenderComponentProcessor<TComponent, TData> : IDataComponentProcessor<TComponent>
{
private readonly Func<TComponent, TData> m_getValueFunc;
private readonly Grid m_grid;
public CellAppenderComponentProcessor(Func<TComponent, TData> func, Grid grid)
{
m_getValueFunc = func;
m_grid = grid;
}
public void Process(TComponent dataComponet)
{
TData data = m_getValueFunc.Invoke(dataComponet);
m_grid.AppendCell(data.ToString());
}
}
Ora è il momento di mettere tutti i pezzi insieme:
Grid grid = new Grid(m_fabbricatiListView);
grid.Initialize();
grid.AppendColumn("Rendita catastale");
grid.AppendColumn("Valore fabbricato");
var praticaProcessor = new OneToManyDataComponentRelation<Pratica, Fabbricato>(
pratica => pratica.Fabbricati,
new OneToManyDataComponentRelation<Fabbricato, Titolarita>(
fabbricato => fabbricato.Titolarita,
new MultiComponentProcessor<Titolarita>(
new RowAppenderComponentProcessor<Titolarita>(grid),
new CellAppenderComponentProcessor<Titolarita, Money>(
titolarita => new Money(titolarita.RenditaCatastale), grid),
new CellAppenderComponentProcessor<Titolarita, Money>(
titolarita => new Money(titolarita.ValoreFabbricato), grid))));
Avendo la necessità eseguire più Processor ho creato la classe MultiComponentProcessor:
public class MultiComponentProcessor<T> : IDataComponentProcessor<T>
{
private readonly IDataComponentProcessor<T>[] m_processors;
public MultiComponentProcessor(params IDataComponentProcessor<T>[] processors)
{
m_processors = processors;
}
public void Process(T dataComponet)
{
foreach (var processor in m_processors)
{
processor.Process(dataComponet);
}
}
}
La costruzione degli oggetti che ho scritto sopra non è forse leggibilissima, ma il problema si può risolvere facilmente creando un oggetto Builder con una fluent interface.
Devo riempire una grid con i seguenti dati:
La quale rappresenta un esempio di una lista di fabbricati di una pratica ICI. Ogni riga rappresenta la titolarità di un fabbricato quindi sono rappresentati tre fabbricati di cui il primo con due titolarita.
Come vediamo la rappresentazione dei dati ha un pò di logica ad esempio la prima colonna ha due formattazioni diverse in base ad un valore booleano, poi dalla seconda colonna i dati legati al fabbricato vengono ripetuti se ci sono diverse titolarità ed infine ci sono alcuni importi da visualizzare.
Vediamo la parte di dominio legata ai dati della lista:
Come si vede abbiamo due relazioni uno a molti: Pratica -> Fabbricati e Fabbricato -> Titolarita.
Partiamo dal setup del test di accettazione (per chi non lo sapesse un test di accettazione è un test end to end, cioè testa la feature con tutti i componenti):
[SetUp]
public void SetUp()
{
Pratica praticaIci = new Pratica();
Fabbricato fabbricatoMilano = new Fabbricato
{
Verificato = true,
Progressivo = 1,
Comune = "Milano",
Indirizzo = "Viale Roma"
};
fabbricatoMilano.Titolarita.Add(new Titolarita{ RenditaCatastale = 180.76M, ValoreFabbricato = 18980.70M });
fabbricatoMilano.Titolarita.Add(new Titolarita{ RenditaCatastale = 93.02M, ValoreFabbricato = 8950.20M });
praticaIci.Fabbricati.Add(fabbricatoMilano);
...
}
Ho messo solo il primo per leggibilità. Vediamo il codice del test ora:
[Test]
public void ShouldFillFabbricatiGrid()
{
Grid grid = new Grid();
FabbricatiGridFiller filler = new FabbricatiGridFiller(grid);
filler.Fill(praticaIci);
Assert.That(grid.ColumnHeaders[0].Text, Is.EqualTo("Verif."));
Assert.That(grid[0, 0].Text, Is.EqualTo("SI"));
Assert.That(grid[0, 0].BackColor, Is.EqualTo(Color.LightGreen));
Assert.That(grid[0, 0].FontStyle, Is.EqualTo(FontStyle.Bold));
Assert.That(grid[1, 0].Text, Is.Empty);
Assert.That(grid.ColumnHeaders[1].Text, Is.EqualTo("Progr."));
Assert.That(grid[0, 1].Text, Is.EqualTo("1"));
Assert.That(grid[1, 1].Text, Is.EqualTo("1"));
}
Ho riportato solo una parte delle Assert, ma il concetto spero sia chiaro.
A questo punto facciamo compilare il codice del test creando delle classi senza implementazione, che non riporto.
Il motivo per cui sono partito dal test di accettazione sta nel fatto che mi mi
chiarisce l'obiettivo a cui devo arrivare, ma ora utilizzerò il TDD per arrivare all'implementazione della feature. Quindi non preoccupatevi in questo test non ho usato nessun
Big Design Up Front (BDUF).
Qual è il prossimo passo?
Per rispondere a questa domanda definiamo il principio che vogliamo che il nostro design debba rispettare. Nel mio caso specifico ho molte liste da riempire simili a questa i cui dati sono memorizzati in classi analoghe a Fabbricato, Titolarita ecc. Quindi voglio descrivere il binding dei dati senza dipendere dai dati stessi. Inoltre non voglio utilizzare la reflection, ma un grafo di oggetti che collaborino tra loro per ottenere il risultato.
Partirò dalla relazione tra gli oggetti che contengono i dati (Pratica, Fabbricato, Titolarita). Come si può vedere sono due relazioni uno a molti la prima tra Pratica e Fabbricato, la seconda tra Fabbricato e Titolarita.
Come chiamiamo questo oggetto? OneToManyDataComponentRelation. Gli oggetti come Fabbricato li ho battezzati DataComponent e sono oggetti solitamente mappati con una tabella e che mi arrivano da un ORM di vostra scelta.
Vediamo il test:
[Test]
public void ShouldProcessRelatedComponents()
{
var componentProcessor = this;
var relation = new OneToManyDataComponentRelation<Pratica, Fabbricato>(
pratica => pratica.Fabbricati, componentProcessor);
relation.ProcessRelatedComponents(praticaIci);
Assert.That(componentProcessedCount, Is.EqualTo(2));
}
Se vediamo il codice della classe penso che risulterà più semplice la comprensione del test:
public class OneToManyDataComponentRelation<TOne, TMany>
{
private readonly Func<TOne, IEnumerable<TMany>> m_getRelatedComponentsFunc;
private readonly IDataComponentProcessor<TMany> m_relatedComponentProcessor;
public OneToManyDataComponentRelation(
Func<TOne, IEnumerable<TMany>> getRelatedComponentsFunc,
IDataComponentProcessor<TMany> relatedComponentProcessor)
{
m_getRelatedComponentsFunc = getRelatedComponentsFunc;
m_relatedComponentProcessor = relatedComponentProcessor;
}
public void ProcessRelatedComponents(TOne dataComponent)
{
IEnumerable<TMany> components = m_getRelatedComponentsFunc.Invoke(dataComponent);
foreach (var component in components)
{
m_relatedComponentProcessor.Process(component);
}
}
}
In pratica ho definito una relazione uno a molti tra due tipi in questo Caso Pratica e Fabbricato. E per ogni link della relazione viene eseguito il metodo process dell'interfaccia IDataComponent.
Un'altra cosa interessante è l'inizializzazione del data processor a this e cioè alla classe di test. Questa tecnica si chiama Self Shunt ed è utile quando il test di interazione è molto semplice. Vediamo la classe di test al completo:
[TestFixture]
public class OneToManyDataComponentRelationTests : IDataComponentProcessor<Fabbricato>
{
private int componentProcessedCount;
private Pratica praticaIci;
[SetUp]
public void SetUp()
{
componentProcessedCount = 0;
praticaIci = new Pratica();
praticaIci.Fabbricati.Add(new Fabbricato());
praticaIci.Fabbricati.Add(new Fabbricato());
}
[Test]
public void ShouldProcessRelatedComponents()
{
var componentProcessor = this;
var relation = new OneToManyDataComponentRelation<Pratica, Fabbricato>(
pratica => pratica.Fabbricati, componentProcessor);
relation.ProcessRelatedComponents(praticaIci);
Assert.That(componentProcessedCount, Is.EqualTo(2));
}
void IDataComponentProcessor<Fabbricato>.Process(Fabbricato dataComponet)
{
componentProcessedCount++;
}
}
Ora una cosa che mi piacerebbe è di poter concatenare le relazioni.
new OneToManyDataComponentRelation<Pratica, Fabbricato>(
pratica => pratica.Fabbricati,
new OneToManyDataComponentRelation<Fabbricato, Titolarita>(
fabbricato => fabbricato.Titolarita,
...));
Per ottenere questo risultato basta semplicemente far implementare l'interfaccia IDataComponentProcessor alla nostra relation:
public class OneToManyDataComponentRelation<TOne, TMany> : IDataComponentProcessor<TOne>
{
private readonly Func<TOne, IEnumerable<TMany>> m_getRelatedComponentsFunc;
private readonly IDataComponentProcessor<TMany> m_relatedComponentProcessor;
public OneToManyDataComponentRelation(
Func<TOne, IEnumerable<TMany>> getRelatedComponentsFunc,
IDataComponentProcessor<TMany> relatedComponentProcessor)
{
m_getRelatedComponentsFunc = getRelatedComponentsFunc;
m_relatedComponentProcessor = relatedComponentProcessor;
}
public void Process(TOne dataComponent)
{
IEnumerable<TMany> components = m_getRelatedComponentsFunc.Invoke(dataComponent);
foreach (var component in components)
{
m_relatedComponentProcessor.Process(component);
}
}
}
Scriverò un altro post per la parte rimanente.
venerdì 4 dicembre 2009
Una cosa fastidiosa nell'usare il TDD con Visual Studio è la mancanza di strumenti pensati per chi scrive i test prima del codice.
ReSharper risolve parzialmente il problema; ad esempio quando da un test creiamo la classe sotto test viene creata nello stesso file. Si può spostare in un altro file, ma rimane nel progetto di test.
Cercando in rete ho trovato un plugin di resharper che ovvia al problema: TDD Productivity Plugin for Resharper. Si installa tramite un setupkit ed è compatibile con le versioni 4.5 e 4.1
Ho provato da una classe di test con alt + enter ad usare l'azione Create Class e la classe è stata creata nel progetto referenziato. Molto comodo!
Spero che possa tornare utile anche a voi.
martedì 6 ottobre 2009
Le prodezze di Luka con processing hanno ispirato questo post.
Ci sono videogiochi nel panorama indie in cui il lato artistico è particolarmente curato, e l'abilità del giocatore è secondaria rispetto l'esperienza di gioco.
A questo propostito vi segnalo:
- The Path videogioco horror basato principalmente sull'esplorazione (maggiori info qui).
- Windosill videogioco flash basato sull'interazione con l'ambiente (maggiori info qui).
- Machinarium bellissima l'ambientazione post-apocalitica (maggiori info qui).
Sono lavori che hanno dietro un piccolo gruppo di sviluppatori appassionati, che ritengo meritevoli di essere supportati con l'acquisto di una licenza, molto economica rispetto a titoli più blasonati.
Se ne avete qualcuno da segnalare scrivetelo nei commenti che l'aggiungo alla lista.
sabato 3 ottobre 2009
Ho deciso di iscrivermi al workshop sul design emergente che verrà tenuto da Francesco Cirillo.
A convincermi è stato Matteo che ne parla nel suo blog.
Ho inoltre acquistato e letto il libro The art of unit testing.
Dalla sezione Avoiding overspecification in tests ho imparato che tendo ad abusare degli oggetti mock anche quando sarebbero sufficienti degli stub.
Lo scopo di questo post è di spronare chi fa questo mestiere ad investire sulla propria professionalità e sulla propria tecnica nello scrivere codice.
Purtroppo ho visto diverse volte sviluppatori software con decenni di esperienza non conoscere i concetti più elementari di OOP come l' information hiding.
domenica 6 settembre 2009
Ho visto che è una tendenza diffusa da parte del cliente di preferire contratti a scadenza fissa, budget fisso e scope fisso. In pratica è come se stessi dicendo alla software house che lo svilupperà: fammi questo software con queste funzionalità entro tale data.
Il motivo credo sia soprattutto ingenuità e mancanza di fiducia con l'illusione che un contratto blindato possa garantire il risultato.
Nella pratica succede, che alla fine entrambi gli attori in gioco ci rimettono: la software house è costretta a gonfiare le stime ed essere molto pignola nelle specifiche, il cliente avrà poco controllo perchè ogni richiesta diversa da quella iniziale dovrà essere rinegoziata.
Ma perchè viene quindi preferito rispetto ad altri tipi di contratti?
Perchè ha due grossi vantaggi: è facile da capire e soprattutto è facile da confrontare.
Le aziende spesso hanno un ufficio acquisti che non ha competenze tecniche e quindi quei due vantaggi possono essere fondamentali.
Come potremmo mitigarne gli svantaggi?
Se possibile ridurre la durata del contratto e il numero di funzionalità il più possibile. In modo da ridurre i rischi.
Quali contratti alternativi posso proporre?
Il concetto di base è che una o più variabili in gioco (tempo, scope, feature) siano variabili. Qualche link per approfonfire l'argomento:
I link mi erano stati segnalati da Matteo e Piero
Vi ritrovate in questa descrizione? O riuscite a fare contratti piuttosto flessibili?
martedì 28 aprile 2009
Jacopo mi ha segnalato questo post,
in cui viene spiegato come FriendFeed memorizza su un database MySQL dati schema-less.
L'idea è quella di memorizzare una versione serializzata delle nostre entities, nel caso di FriendFeed in formato JSON.
La tabella ha la seguente struttura:
CREATE TABLE entities (
added_id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
id BINARY(16) NOT NULL,
updated TIMESTAMP NOT NULL,
body MEDIUMBLOB,
UNIQUE KEY (id),
KEY (updated)
) ENGINE=InnoDB;
La cosa, che trovo, interessante è la motivazione che ha spinto ad adottare tale struttura cito dal post:
As our database has grown, we have tried to iteratively deal with the scaling issues that come with rapid growth. We did the typical things, like using read slaves and memcache to increase read throughput and sharding our database to improve write throughput. However, as we grew, scaling our existing features to accomodate more traffic turned out to be much less of an issue than adding new features.
In particular, making schema changes or adding indexes to a database with more than 10 - 20 million rows completely locks the database for hours at a time. Removing old indexes takes just as much time, and not removing them hurts performance because the database will continue to read and write to those unused blocks on every INSERT, pushing important blocks out of memory.
Ayende ne parla nel post Designing a document database: What next? ed
esiste anche un'implementazione open source chiamata CouchDB
Questa idea è un'eventuale alternativa ai classici database relazionali, nel caso in cui ci si trovi in condizioni analoghe.