AntonioGanci

Il blog di Antonio Ganci
posts - 150, comments - 347, trackbacks - 103

lunedì 17 novembre 2008

Il codice è il design

Ringrazio il mio collega per avermi suggerito i tre essays su Cose as Design di Jack W. Reeves. mi hanno dato alcuni spunti di riflessione e ne consiglio vivamente la lettura.

L'autore sostiene ed argomenta un concetto secondo me tanto semplice quanto vero: Il design è il codice. L'articolo è stato pubblicato nel 1992 sul C++ Journal, un periodo in cui era diffusa l'idea che il coding fosse un'attività di basso profilo. Un'idea passata quasi inosservata e poi ripresa da Ward Cunningham in questa pagina del wiki.

Alcune considerazioni:

Se il codice è il design, qual è il manufatto e chi lo costruisce?

E' il file eseguibile e viene costruito dal compilatore/linker (notare l'uso del termine build anche in VS)

I class diagram, UML, ecc. cosa sono?

Sono sempre strumenti di design, ma che danno una rappresentazione di più alto livello.

Ed il TDD come si inserisce in questa visione?

Il TDD serve a validare il design, cioè che il codice si comporti nel modo previsto.

In sostanza il codice è il design più dettagliato possibile quello che rende possibile la creazione fisica del nostro software. Questo spiega anche il perchè sia così difficile creare codice di qualità, non è un'attività di manovalanza, ma un'attività ingegneristica.

posted @ lunedì 17 novembre 2008 8.53 | Feedback (6) | Filed Under [ Extreme Programming ]

martedì 21 ottobre 2008

Guida galattica per la lettura degli annunci immobiliari di Milano

Chi ha dovuto cercare un appartamento in affitto a Milano sa di cosa sto parlando, per gli altri, sembreranno un cumulo di stronzate.

Sostituire le parole in grassetto che trovate in un annuncio con il corrispettivo a destra:

Centralissimo: probabilmente in una cabina telefonica c'è più spazio
Luminossisimo:  Sesto piano senza ascensore.
Ideale per studenti: l'arredamento è stato acquistato in una discarica ed una persona sola non se lo può permettere
Ideale per coppie: Una persona sola non se lo può permettere e il padrone non vuole studenti.
Zona XXX (qualunque nome): Come minimo è a Gessate.
Loft: negozio di macelleria trasformato in apparamento.
Solo referenziati: non vogliamo extracomunitari
Palazzo signorile: è stato costruito ai tempi di Napoleone e non è mai stato ristrutturato da allora.
Appena ristrutturato: hanno dato il bianco.
Come nuovo: vedi sopra
Con posto auto: vedi Zona XXX.
Comodo ai mezzi: l'aereo non vale.

Per l'acquisto:
Ottimo per investimento:
probabilmente demoliranno l'edificio tra sei mesi.

Se ne avete altri aggiungeteli ai commenti che aggiornerò il glossario.

posted @ martedì 21 ottobre 2008 19.21 | Feedback (4) | Filed Under [ Tips ]

Il refactoring che non vedrete mai in un libro.

Aprite visual studio (o l'ide che preferite; si anche notepad o TextEdit vanno bene).
Caricate la parte di codice di cui andate più orgogliosi, su cui avete riflettuto per settimane fino ad ottenere la più perfetta sinfonia di design che la vostra mente potesse concepire.
Respirate.
Cancellate.
Riscrivete lo stesso codice in un altro modo.
I vostri colleghi ringrazieranno.

Fonte: Throwing away code

posted @ martedì 21 ottobre 2008 18.54 | Feedback (3) | Filed Under [ Extreme Programming ]

sabato 23 agosto 2008

Scrivere uno unit test per l'accesso esclusivo ad una risorsa condivisa da diversi thread - Alcune considerazioni

Vorrei rispondere ad alcuni commenti del mio precedente post. Il primo di Claudio Maccari

Nel tuo esempio crei un thread per ogni OrderThread e non credo sia la cosa più efficente da fare. meglio usare ThreadPool.QueueUserWorkItem Io scriverei così all'interno di OrderThread

public void Start()
{
ThreadPool.QueueUserWorkItem(delegate { _broker.PlaceOrder(_order); });
}

ma in questo caso non saprei come scrivere uno unit-test adeguato. tu come faresti ?

Per prima cosa cambiamo l'implementazione della classe OrderThread in modo che utilizzi un ThreadPool:

    class OrderThread
    {
        private readonly IBroker _broker;
        private readonly Order _order;
        private Thread _thread;
 
        public OrderThread(IBroker broker, Order order)
        {
            _broker = broker;
            _order = order;
        }
 
        public bool IsWaiting
        {
            get
            {
                if (_thread == null) return false;
                return (_thread.ThreadState & ThreadState.WaitSleepJoin) > 0;
            }
        }
 
        public void Start()
        {
            ThreadPool.QueueUserWorkItem(delegate { PlaceOrder(); });
        }
 
        private void PlaceOrder()
        {
            _thread = Thread.CurrentThread;
            _broker.PlaceOrder(_order);
        }
    }

Ho dovuto modificare l'implementazione della property IsWaiting perchè i thread creati dal ThreadPool sono Background thread.

Ora lanciamo il test che è rimasto uguale rispetto al precedente post. L'output generato è il seguente:

TestCase 'OrderThreadTests.Access_To_The_Broker_Should_Be_Synchronized' failed: Equal assertion failed: [[1]]!=[[2]]

In pratica entrambi i thread sono all'interno del metodo PlaceOrder del Broker! Abbiamo una race condition. Questo è normale perchè non abbiamo sincronizzato l'accesso al Broker.

Modifichiamo quindi il metodo privato PlaceOrder aggiungendo una lock:

        private void PlaceOrder()
        {
            _thread = Thread.CurrentThread;
            lock(_broker)
            {
                _broker.PlaceOrder(_order);
            }
        }

Ora rilanciamo il test:

[success] OrderThreadTests.Access_To_The_Broker_Should_Be_Synchronized

Ed ecco che abbiamo cambiato l'implentazione della classe OrderTest senza modificare il test. Ci si potrebbe chiedere perchè non ho utilizzato subito un ThreadPool, il motivo è che nell'applicazione questi thread seguono anche lo stato dell'ordine ed hanno quindi una vita molto lunga (un ordine può essere eseguito dopo qualche ora che è stato immesso) e mi serviva avere un riferimento al thread per sincronizzarli tra di loro.

Alcune considerazioni sullo unit testing per il multithreading:

  • Lo unit test NON garantisce l'assenza di problemi di sincronizzazione come in generale non garantisce l'assenza di bug. In questo caso però modificando l'implementazione con quella proposta di Claudio ha fatto subito emergere il problema di essersi dimenticato il lock
  • Il TDD in questi casi serve soprattutto a far emergere un'architettura facilmente testabile
  • Il Multithreading aggiunge molta complessità al codice questo rende difficile anche la scrittura dei test i quali potrebbero dipendere dal pc su cui girano perchè a chi li ha scritti è sfuggita qualche condizione di concorrenza.

Vorrei infine segnalare il post TotT: Sleeping != Synchronization del Google Testing Blog che tratta lo stesso argomento, il codice è scritto in ruby ma non dovrebbero esserci molte difficoltà a comprenderlo.

posted @ sabato 23 agosto 2008 11.40 | Feedback (0) |

lunedì 18 agosto 2008

Google Testing Blog

Vorrei segnalare il blog Google Testing Blog in particolare dall'ultimo post:

Singletons are nothing more than global state. Global state makes it so your objects can secretly get hold of things which are not declared in their APIs, and, as a result, Singletons make your APIs into pathological liars.

Questo l'ho anche visto quando si abusa dei framework di IoC risalire alle dipendenze di un oggetto diventa un'ardua impresa.

posted @ lunedì 18 agosto 2008 8.54 | Feedback (3) |

domenica 17 agosto 2008

Come scrivere uno unit test per controllare l'accesso esclusivo ad una risorsa condivisa da diversi thread

Nel mio precedente post ho affrontato il problema di come sviluppare usando il TDD un'applicazione multithreading.

In questo post, cercherò di approfondire su come testare la sincronizzazione per l'accesso ad una risorsa condivisa.

L'obiettivo che voglio raggiungere è scrivere un test che fallisca se non applico un meccanismo di sincronizzazione (in questo caso la lock) per l'accesso alla risorsa e abbia successo quando viene introdotta la sincronizzazione. Il fatto che il test abbia successo deve essere sistematico e non deve esserci nessun elemento di casualità come l'ordine di schedulazione la velocità del processore.

Vediamo di descrivere lo scenario. La risorsa condivisa è il Broker la cui responsabilità è quella di immettere sul mercato azionario un ordine di acquisto o vendita di azioni. La classe Broker implementa la seguente interfaccia:

    interface IBroker
    {
        void PlaceOrder(Order order);
    }

La classe Order che non riporterò contiene i dati necessari per immettere l'ordine come il prezzo, la quantità di azioni, ecc.

L'ultimo elemento di questo scenario è la classe OrderThread, la sua responsabilità è quella di eseguire il metodo PlaceOrder in un thread creato da lei.

    class OrderThread
    {
        private readonly IBroker _broker;
        private readonly Order _order;
 
        public OrderThread(IBroker broker, Order order)
        {
            _broker = broker;
            _order = order;
        }
 
        public void Start()
        {
            Thread thread = new Thread(PlaceOrder);
            thread.Start();
        }
 
        private void PlaceOrder()
        {
            _broker.PlaceOrder(_order);
        }
    }

Come si può notare alla chiamata PlaceOrder non viene usato nessun meccanismo di sincronizzazione. Quindi due istanze di OrderThread che utilizzino lo stesso broker potrebbero chiamare contemporaneamente il metodo PlaceOrder dando luogo ad una race condition. Vogliamo scrivere un test che abbia successo se la race condition non possa verificarsi e fallisca altrimenti.

Per ottenere il risultato scriviamo un'implementazione dell'interfaccia IBroker pilotabile dal nostro test che ci permetta di simulare lo scenario in cui due OrderThread cerchino di chiamare contemporaneamente il metodo PlaceOrder.

    class SleepingBroker : IBroker
    {
        readonly ManualResetEvent _resetEvent = new ManualResetEvent(false);
 
        public int CallingPlaceOrderCount { get; set;}
 
        public void PlaceOrder(Order order)
        {
            CallingPlaceOrderCount++;
            _resetEvent.WaitOne();
            CallingPlaceOrderCount--;
        }
 
        public void WakeUp()
        {
            _resetEvent.Set();
        }
    }

Il metodo SleepingBroker.PlaceOrder ci permette di far durare la chiamata a PlaceOrder il tempo necessario per creare una race condition. Se il valore della property CallingPlaceOrderCount è maggiore di uno significa che più di un trade sta usando il Broker e quindi il test deve fallire.

Proviamo a questo punto a scrivere il test:

        [Test]
        public void Access_To_The_Broker_Should_Be_Synchronized()
        {
            Order order1 = new Order();
            Order order2 = new Order();
            SleepingBroker broker = new SleepingBroker();
            OrderThread thread1 = new OrderThread(broker, order1);
            OrderThread thread2 = new OrderThread(broker, order2);
 
            thread1.Start();
            thread2.Start();
 
            Assert.AreEqual(1, broker.CallingPlaceOrderCount);
 
            broker.WakeUp();
        }

In questo test abbiamo creato due OrderThread che utilizzano lo stesso Broker (in questo caso SleepingBroker). I due thread vengono fatti partire e controlliamo che effettivamente solo uno sia all'interno del metodo PlaceOrder.

Il test apparentemente corretto in realtà contiene un errore concettuale. L'errore è che potrebbe fallire o avere successo in maniera del tutto casuale perchè dipende dalla velocità di creazione ed esecuzione dei thread al momento in cui si fa la Assert.

Il valore di CallingPlaceOrderCount potrebbe quindi essere 0, 1 oppure due. Zero nel caso in cui entrambi i thread siano stati creati ma al momento della Assert la chiamata a PlaceOrder non sia ancora avvenuta. Uno nel caso che uno dei due sia entrato nella PlaceOrder. Due nel caso in cui entrambi siano nella PlaceOrder.

Per come abbiamo scritto il test questo non è determinabile. Quindi modifichiamolo sincronizzandoci nel modo corretto. Per far questo ci viene in aiuto la property ThreadState, nel caso in cui il valore è impostato su WaitSleepJoin come descritto nella MSDN:

The thread is blocked. This could be the result of calling Thread..::.Sleep or Thread..::.Join, of requesting a lock — for example, by calling Monitor..::.Enter or Monitor..::.Wait — or of waiting on a thread synchronization object such as ManualResetEvent.

Quindi se entrambi i thread sono in WaitSleepJoin siamo sicuri di poter eseguire la Assert su CallingPlaceOrderCount nel momento corretto.

Per controllare lo stato del thread Aggiungiamo la Property IsWaiting alla classe OrderThread.

    class OrderThread
    {
        private readonly IBroker _broker;
        private readonly Order _order;
        private Thread _thread;
 
        public OrderThread(IBroker broker, Order order)
        {
            _broker = broker;
            _order = order;
        }
 
        public bool IsWaiting
        {
            get
            {
                if (_thread == null)
                {
                    return false;
                }
                return _thread.ThreadState == ThreadState.WaitSleepJoin;
            }
        }
 
        public void Start()
        {
            _thread = new Thread(PlaceOrder);
            _thread.Start();
        }
 
        private void PlaceOrder()
        {
            _broker.PlaceOrder(_order);
        }
    }

Quindi ora aggiungiamo il codice nel test che attenda che per entrambi i Thread IsWaiting sia a true.

        [Test]
        public void Access_To_The_Broker_Should_Be_Synchronized()
        {
            Order order1 = new Order();
            Order order2 = new Order();
            SleepingBroker broker = new SleepingBroker();
            OrderThread thread1 = new OrderThread(broker, order1);
            OrderThread thread2 = new OrderThread(broker, order2);
 
            thread1.Start();
            thread2.Start();
 
            while(!thread1.IsWaiting || !thread2.IsWaiting)
            {
                Thread.Sleep(0);
            }
 
            Assert.AreEqual(1, broker.CallingPlaceOrderCount);
 
            broker.WakeUp();
        }

In questo modo abbiamo tolto l'elemento di casualità spiegato prima. Se non modifichiamo il codice della classe OrderThread il test fallirà in modo sistematico:

TestCase 'OrderThreadTests.Access_To_The_Broker_Should_Be_Synchronized' failed: Equal assertion failed: [[1]]!=[[2]]

Aggiungiamo a questo punto la lock prima della chiamata di PlaceOrder nella classe OrderThread:

        private void PlaceOrder()
        {
            lock(_broker)
            {
                _broker.PlaceOrder(_order);
            }
        }

Proviamo ora a lanciare nuovamente il test ed ecco finalmente la conferma che i due thread sono sincronizzati:

Found 1 tests
[success] OrderThreadTests.Access_To_The_Broker_Should_Be_Synchronized

Il motivo per cui il test passa è che in questo caso sono entrambi in waiting ma uno è all'interno del metodo PlaceOrder mentre l'altro è fermo sulla lock.

L'esempio che ho fatto si può adattare facilmente anche ad altri scenari, in quanto cambieranno gli attori, ma i concetti sono gli stessi.

Feedback?

posted @ domenica 17 agosto 2008 12.46 | Feedback (12) | Filed Under [ Extreme Programming ]

giovedì 17 luglio 2008

How I got Started in Software Development

Dopo la provocazione di Gianluca non potevo esimermi dal rispondere ;-).

A quale età hai cominciato a programmare?

Quinta elementare: lessi un libro della casa editrice Jackson (qualcuno la ricorda?) la quale introduceva agli elementi di base sulla programmazione

Come hai cominciato a programmare?

Su un foglio di carta. Scrivevo il listato rigorosamente in basic e poi lo simulavo manualmente.

Qual’è stato il tuo primo linguaggio di programmazione?

Basic del Commdore64.

Qual’è stato il primo programma vero che hai scritto?

Un gestionale in Foxpro per il mio professore di elettronica in quinta superiore.

Quali linguaggi hai usato da quando hai cominciato a programmare?

Turbo Pascal, Basic (nelle varie incarnazioni GWBASIC, QuickBasic, ...), Assembler, C, C++, Prolog, VB, C# e tanti altri che nemmeno ricordo

Quando è stato il tuo primo vero lavoro da programmatore?

Vedi sopra

Con il senno di poi, rifaresti lo stesso il programmatore? Ricominceresti a programmare?

Certamente. What else? :-)

Se ci fosse una cosa che hai imparato nella tua carriera e che vorresti dire ai giovani programmatori, cosa diresti?

Studiate, leggete e soprattutto imparate dal codice scritto da altri. Un'ultima cosa è grazie ai clienti se pagate l'affitto quindi non trattateli troppo male.

Qual’è la cosa più divertente che hai programmato?

Alcuni videogiochi realizzati con il mitico S.E.U.C.K. (anche se non si può dire verà programmazione) e la gestione multiplayer di un videogioco tramite le DirectPlay

Chi tirare in ballo ora?

Papo

Roberto Valenti

Pierre Greborio

Rosalba Fiore

posted @ giovedì 17 luglio 2008 22.36 | Feedback (1) |

Tell, Don't Ask - alcuni spunti di riflessioni su come migliorare il disegno delle proprie applicazioni

Ho letto il post The Flawed Theory Behind Unit Testing di Michael Feathers e mi ha fornito alcuni spunti interessanti di riflessione. In particolare:

John Nolan, gave his developers a challenge: write OO code with no getters. Whenever possible, tell another object to do something rather than ask. In the process of doing this, they noticed that their code became supple and easy to change.

Ho rivolto la questione al team con cui sto lavorando ed il coach ha posto la seguente domanda:

Una cosa che continuo a chiedermi a proposito dell "tell, don't ask" è la maniera in cui vengono normalmente realizzate le pagine web: tu hai un template a cui passi oggetti a cui chiedi varie informazioni. E' tutto un "ask, don't tell", che fa sì che gli oggetti che passi alla view siano, alla fine, oggetti di dominio, i quali finiscono per essere poco più che contenitori di dati estratti dal DB. Questo è quello che si fa di solito. La domanda che vi pongo è: come si potrebbe fare altrimenti? A partire dalla view: come puoi fare una view che applica il "tell, don't ask"?

Un altro link interessante su questo concetto è Tell, Don't Ask.

Alla domanda di prima non ho una risposta, ma ci sto riflettendo. Inoltre voglio approfondire l'argomento e verificare se l'approccio è efficace. Questo implica ragionare con gli oggetti in funzione di cosa fanno piuttosto che dipendere da quale stato siano.

In termini di TDD significa prediligere i test d'interazione piuttosto che quelli di stato e quindi l'utilizzo intensivo dei mock objects.

Cosa ne pensate?

posted @ giovedì 17 luglio 2008 22.00 | Feedback (0) |

mercoledì 18 giugno 2008

Fino a che punto siamo disposti a cambiare nel lavoro?

A parole tutti vorremmo cambiare, è un mantra, che spesso si sente in tv soprattutto in periodo elettorale. Sembrerebbe che la natura umana stia a suo agio nel cambiamento.
In realtà le cose funzionano in maniera completamente diversa, noi per natura, non vogliamo cambiare; il cambiamento ci costa fatica e per istinto cerchiamo di osteggiarlo.
Riportando queste divagazioni al nostro mestiere di sviluppatori, magari con qualche anno di lavoro alle spalle, proviamo a rispondere alla domanda: Fino a che punto siamo disposti a cambiare? E con cambiare intendo:

  • Metodo di approccio allo sviluppo software (ad es: passare da Waterfall ad xp? e viceversa?)
  • Tecnologia (sviluppo da diversi anni in .NET passerei a java? e viceversa?)
  • Sistema Operativo (ho sempre usato windows xp, passerei a Vista e a linux? e viceversa?)
  • Altro?
Quale la cosa che non cambierei per nulla al mondo? (a meno di eventi che ci costringano senza alternative).

A questo proposito segnalo l'interessante post sul cambiamento che aveva segnalato lorenzo in questo post e che in parte ha ispirato questo post.
Cosa ne pensate?

posted @ mercoledì 18 giugno 2008 15.05 | Feedback (7) |

lunedì 9 giugno 2008

Object-Oriented Reengineering Patterns

Segnalo il libro Object-Oriented Reengineering Patterns scaricabile gratuitamente come pdf.

Non ne ho ancora terminato la lettura, ma per ora mi ha fornito alcuni spunti interessanti.

Affronta il problema di reingegnerizzare un software esistente e fornisce come strumenti una serie di pattern.

posted @ lunedì 9 giugno 2008 20.18 | Feedback (7) |

Powered by: